text: #pragma once
  /*
  CSV for C++, version 2.1.0
  https://github.com/vincentlaucsb/csv-parser
  
  MIT License
  
  Copyright (c) 2017-2020 Vincent La
  
  Permission is hereby granted, free of charge, to any person obtaining a copy
  of this software and associated documentation files (the "Software"), to deal
  in the Software without restriction, including without limitation the rights
  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  copies of the Software, and to permit persons to whom the Software is
  furnished to do so, subject to the following conditions:
  
  The above copyright notice and this permission notice shall be included in all
  copies or substantial portions of the Software.
  
  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  SOFTWARE.
  */
  
  #ifndef CSV_HPP
  #define CSV_HPP
  
  /** @file
   *  @brief Defines functionality needed for basic CSV parsing
   */
  
  
  #include <algorithm>
  #include <deque>
  #include <fstream>
  #include <iterator>
  #include <memory>
  #include <mutex>
  #include <thread>
  #include <sstream>
  #include <string>
  #include <vector>
  
  /* Copyright 2017 https://github.com/mandreyel
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy of this
   * software and associated documentation files (the "Software"), to deal in the Software
   * without restriction, including without limitation the rights to use, copy, modify,
   * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so, subject to the following
   * conditions:
   *
   * The above copyright notice and this permission notice shall be included in all copies
   * or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
   * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
   * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  
  #ifndef MIO_MMAP_HEADER
  #define MIO_MMAP_HEADER
  
  // #include "mio/page.hpp"
  /* Copyright 2017 https://github.com/mandreyel
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy of this
   * software and associated documentation files (the "Software"), to deal in the Software
   * without restriction, including without limitation the rights to use, copy, modify,
   * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so, subject to the following
   * conditions:
   *
   * The above copyright notice and this permission notice shall be included in all copies
   * or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
   * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
   * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  
  #ifndef MIO_PAGE_HEADER
  #define MIO_PAGE_HEADER
  
  #ifdef _WIN32
  # include <windows.h>
  #else
  # include <unistd.h>
  #endif
  
  namespace mio {
  
  /**
   * This is used by `basic_mmap` to determine whether to create a read-only or
   * a read-write memory mapping.
   */
  enum class access_mode
  {
      read,
      write
  };
  
  /**
   * Determines the operating system's page allocation granularity.
   *
   * On the first call to this function, it invokes the operating system specific syscall
   * to determine the page size, caches the value, and returns it. Any subsequent call to
   * this function serves the cached value, so no further syscalls are made.
   */
  inline size_t page_size()
  {
      static const size_t page_size = []
      {
  #ifdef _WIN32
          SYSTEM_INFO SystemInfo;
          GetSystemInfo(&SystemInfo);
          return SystemInfo.dwAllocationGranularity;
  #else
          return sysconf(_SC_PAGE_SIZE);
  #endif
      }();
      return page_size;
  }
  
  /**
   * Alligns `offset` to the operating's system page size such that it subtracts the
   * difference until the nearest page boundary before `offset`, or does nothing if
   * `offset` is already page aligned.
   */
  inline size_t make_offset_page_aligned(size_t offset) noexcept
  {
      const size_t page_size_ = page_size();
      // Use integer division to round down to the nearest page alignment.
      return offset / page_size_ * page_size_;
  }
  
  } // namespace mio
  
  #endif // MIO_PAGE_HEADER
  
  
  #include <iterator>
  #include <string>
  #include <system_error>
  #include <cstdint>
  
  #ifdef _WIN32
  # ifndef WIN32_LEAN_AND_MEAN
  #  define WIN32_LEAN_AND_MEAN
  # endif // WIN32_LEAN_AND_MEAN
  # include <windows.h>
  #else // ifdef _WIN32
  # define INVALID_HANDLE_VALUE -1
  #endif // ifdef _WIN32
  
  namespace mio {
  
  // This value may be provided as the `length` parameter to the constructor or
  // `map`, in which case a memory mapping of the entire file is created.
  enum { map_entire_file = 0 };
  
  #ifdef _WIN32
  using file_handle_type = HANDLE;
  #else
  using file_handle_type = int;
  #endif
  
  // This value represents an invalid file handle type. This can be used to
  // determine whether `basic_mmap::file_handle` is valid, for example.
  const static file_handle_type invalid_handle = INVALID_HANDLE_VALUE;
  
  template<access_mode AccessMode, typename ByteT>
  struct basic_mmap
  {
      using value_type = ByteT;
      using size_type = size_t;
      using reference = value_type&;
      using const_reference = const value_type&;
      using pointer = value_type*;
      using const_pointer = const value_type*;
      using difference_type = std::ptrdiff_t;
      using iterator = pointer;
      using const_iterator = const_pointer;
      using reverse_iterator = std::reverse_iterator<iterator>;
      using const_reverse_iterator = std::reverse_iterator<const_iterator>;
      using iterator_category = std::random_access_iterator_tag;
      using handle_type = file_handle_type;
  
      static_assert(sizeof(ByteT) == sizeof(char), "ByteT must be the same size as char.");
  
  private:
      // Points to the first requested byte, and not to the actual start of the mapping.
      pointer data_ = nullptr;
  
      // Length--in bytes--requested by user (which may not be the length of the
      // full mapping) and the length of the full mapping.
      size_type length_ = 0;
      size_type mapped_length_ = 0;
  
      // Letting user map a file using both an existing file handle and a path
      // introcudes some complexity (see `is_handle_internal_`).
      // On POSIX, we only need a file handle to create a mapping, while on
      // Windows systems the file handle is necessary to retrieve a file mapping
      // handle, but any subsequent operations on the mapped region must be done
      // through the latter.
      handle_type file_handle_ = INVALID_HANDLE_VALUE;
  #ifdef _WIN32
      handle_type file_mapping_handle_ = INVALID_HANDLE_VALUE;
  #endif
  
      // Letting user map a file using both an existing file handle and a path
      // introcudes some complexity in that we must not close the file handle if
      // user provided it, but we must close it if we obtained it using the
      // provided path. For this reason, this flag is used to determine when to
      // close `file_handle_`.
      bool is_handle_internal_;
  
  public:
      /**
       * The default constructed mmap object is in a non-mapped state, that is,
       * any operation that attempts to access nonexistent underlying data will
       * result in undefined behaviour/segmentation faults.
       */
      basic_mmap() = default;
  
  #ifdef __cpp_exceptions
      /**
       * The same as invoking the `map` function, except any error that may occur
       * while establishing the mapping is wrapped in a `std::system_error` and is
       * thrown.
       */
      template<typename String>
      basic_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file)
      {
          std::error_code error;
          map(path, offset, length, error);
          if(error) { throw std::system_error(error); }
      }
  
      /**
       * The same as invoking the `map` function, except any error that may occur
       * while establishing the mapping is wrapped in a `std::system_error` and is
       * thrown.
       */
      basic_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file)
      {
          std::error_code error;
          map(handle, offset, length, error);
          if(error) { throw std::system_error(error); }
      }
  #endif // __cpp_exceptions
  
      /**
       * `basic_mmap` has single-ownership semantics, so transferring ownership
       * may only be accomplished by moving the object.
       */
      basic_mmap(const basic_mmap&) = delete;
      basic_mmap(basic_mmap&&);
      basic_mmap& operator=(const basic_mmap&) = delete;
      basic_mmap& operator=(basic_mmap&&);
  
      /**
       * If this is a read-write mapping, the destructor invokes sync. Regardless
       * of the access mode, unmap is invoked as a final step.
       */
      ~basic_mmap();
  
      /**
       * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows,
       * however, a mapped region of a file gets its own handle, which is returned by
       * 'mapping_handle'.
       */
      handle_type file_handle() const noexcept { return file_handle_; }
      handle_type mapping_handle() const noexcept;
  
      /** Returns whether a valid memory mapping has been created. */
      bool is_open() const noexcept { return file_handle_ != invalid_handle; }
  
      /**
       * Returns true if no mapping was established, that is, conceptually the
       * same as though the length that was mapped was 0. This function is
       * provided so that this class has Container semantics.
       */
      bool empty() const noexcept { return length() == 0; }
  
      /** Returns true if a mapping was established. */
      bool is_mapped() const noexcept;
  
      /**
       * `size` and `length` both return the logical length, i.e. the number of bytes
       * user requested to be mapped, while `mapped_length` returns the actual number of
       * bytes that were mapped which is a multiple of the underlying operating system's
       * page allocation granularity.
       */
      size_type size() const noexcept { return length(); }
      size_type length() const noexcept { return length_; }
      size_type mapped_length() const noexcept { return mapped_length_; }
  
      /** Returns the offset relative to the start of the mapping. */
      size_type mapping_offset() const noexcept
      {
          return mapped_length_ - length_;
      }
  
      /**
       * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping
       * exists.
       */
      template<
          access_mode A = AccessMode,
          typename = typename std::enable_if<A == access_mode::write>::type
      > pointer data() noexcept { return data_; }
      const_pointer data() const noexcept { return data_; }
  
      /**
       * Returns an iterator to the first requested byte, if a valid memory mapping
       * exists, otherwise this function call is undefined behaviour.
       */
      template<
          access_mode A = AccessMode,
          typename = typename std::enable_if<A == access_mode::write>::type
      > iterator begin() noexcept { return data(); }
      const_iterator begin() const noexcept { return data(); }
      const_iterator cbegin() const noexcept { return data(); }
  
      /**
       * Returns an iterator one past the last requested byte, if a valid memory mapping
       * exists, otherwise this function call is undefined behaviour.
       */
      template<
          access_mode A = AccessMode,
          typename = typename std::enable_if<A == access_mode::write>::type
      > iterator end() noexcept { return data() + length(); }
      const_iterator end() const noexcept { return data() + length(); }
      const_iterator cend() const noexcept { return data() + length(); }
  
      /**
       * Returns a reverse iterator to the last memory mapped byte, if a valid
       * memory mapping exists, otherwise this function call is undefined
       * behaviour.
       */
      template<
          access_mode A = AccessMode,
          typename = typename std::enable_if<A == access_mode::write>::type
      > reverse_iterator rbegin() noexcept { return reverse_iterator(end()); }
      const_reverse_iterator rbegin() const noexcept
      { return const_reverse_iterator(end()); }
      const_reverse_iterator crbegin() const noexcept
      { return const_reverse_iterator(end()); }
  
      /**
       * Returns a reverse iterator past the first mapped byte, if a valid memory
       * mapping exists, otherwise this function call is undefined behaviour.
       */
      template<
          access_mode A = AccessMode,
          typename = typename std::enable_if<A == access_mode::write>::type
      > reverse_iterator rend() noexcept { return reverse_iterator(begin()); }
      const_reverse_iterator rend() const noexcept
      { return const_reverse_iterator(begin()); }
      const_reverse_iterator crend() const noexcept
      { return const_reverse_iterator(begin()); }
  
      /**
       * Returns a reference to the `i`th byte from the first requested byte (as returned
       * by `data`). If this is invoked when no valid memory mapping has been created
       * prior to this call, undefined behaviour ensues.
       */
      reference operator[](const size_type i) noexcept { return data_[i]; }
      const_reference operator[](const size_type i) const noexcept { return data_[i]; }
  
      /**
       * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
       * reason is reported via `error` and the object remains in a state as if this
       * function hadn't been called.
       *
       * `path`, which must be a path to an existing file, is used to retrieve a file
       * handle (which is closed when the object destructs or `unmap` is called), which is
       * then used to memory map the requested region. Upon failure, `error` is set to
       * indicate the reason and the object remains in an unmapped state.
       *
       * `offset` is the number of bytes, relative to the start of the file, where the
       * mapping should begin. When specifying it, there is no need to worry about
       * providing a value that is aligned with the operating system's page allocation
       * granularity. This is adjusted by the implementation such that the first requested
       * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
       * `offset` from the start of the file.
       *
       * `length` is the number of bytes to map. It may be `map_entire_file`, in which
       * case a mapping of the entire file is created.
       */
      template<typename String>
      void map(const String& path, const size_type offset,
              const size_type length, std::error_code& error);
  
      /**
       * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
       * reason is reported via `error` and the object remains in a state as if this
       * function hadn't been called.
       *
       * `path`, which must be a path to an existing file, is used to retrieve a file
       * handle (which is closed when the object destructs or `unmap` is called), which is
       * then used to memory map the requested region. Upon failure, `error` is set to
       * indicate the reason and the object remains in an unmapped state.
       * 
       * The entire file is mapped.
       */
      template<typename String>
      void map(const String& path, std::error_code& error)
      {
          map(path, 0, map_entire_file, error);
      }
  
      /**
       * Establishes a memory mapping with AccessMode. If the mapping is
       * unsuccesful, the reason is reported via `error` and the object remains in
       * a state as if this function hadn't been called.
       *
       * `handle`, which must be a valid file handle, which is used to memory map the
       * requested region. Upon failure, `error` is set to indicate the reason and the
       * object remains in an unmapped state.
       *
       * `offset` is the number of bytes, relative to the start of the file, where the
       * mapping should begin. When specifying it, there is no need to worry about
       * providing a value that is aligned with the operating system's page allocation
       * granularity. This is adjusted by the implementation such that the first requested
       * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
       * `offset` from the start of the file.
       *
       * `length` is the number of bytes to map. It may be `map_entire_file`, in which
       * case a mapping of the entire file is created.
       */
      void map(const handle_type handle, const size_type offset,
              const size_type length, std::error_code& error);
  
      /**
       * Establishes a memory mapping with AccessMode. If the mapping is
       * unsuccesful, the reason is reported via `error` and the object remains in
       * a state as if this function hadn't been called.
       *
       * `handle`, which must be a valid file handle, which is used to memory map the
       * requested region. Upon failure, `error` is set to indicate the reason and the
       * object remains in an unmapped state.
       * 
       * The entire file is mapped.
       */
      void map(const handle_type handle, std::error_code& error)
      {
          map(handle, 0, map_entire_file, error);
      }
  
      /**
       * If a valid memory mapping has been created prior to this call, this call
       * instructs the kernel to unmap the memory region and disassociate this object
       * from the file.
       *
       * The file handle associated with the file that is mapped is only closed if the
       * mapping was created using a file path. If, on the other hand, an existing
       * file handle was used to create the mapping, the file handle is not closed.
       */
      void unmap();
  
      void swap(basic_mmap& other);
  
      /** Flushes the memory mapped page to disk. Errors are reported via `error`. */
      template<access_mode A = AccessMode>
      typename std::enable_if<A == access_mode::write, void>::type
      sync(std::error_code& error);
  
      /**
       * All operators compare the address of the first byte and size of the two mapped
       * regions.
       */
  
  private:
      template<
          access_mode A = AccessMode,
          typename = typename std::enable_if<A == access_mode::write>::type
      > pointer get_mapping_start() noexcept
      {
          return !data() ? nullptr : data() - mapping_offset();
      }
  
      const_pointer get_mapping_start() const noexcept
      {
          return !data() ? nullptr : data() - mapping_offset();
      }
  
      /**
       * The destructor syncs changes to disk if `AccessMode` is `write`, but not
       * if it's `read`, but since the destructor cannot be templated, we need to
       * do SFINAE in a dedicated function, where one syncs and the other is a noop.
       */
      template<access_mode A = AccessMode>
      typename std::enable_if<A == access_mode::write, void>::type
      conditional_sync();
      template<access_mode A = AccessMode>
      typename std::enable_if<A == access_mode::read, void>::type conditional_sync();
  };
  
  template<access_mode AccessMode, typename ByteT>
  bool operator==(const basic_mmap<AccessMode, ByteT>& a,
          const basic_mmap<AccessMode, ByteT>& b);
  
  template<access_mode AccessMode, typename ByteT>
  bool operator!=(const basic_mmap<AccessMode, ByteT>& a,
          const basic_mmap<AccessMode, ByteT>& b);
  
  template<access_mode AccessMode, typename ByteT>
  bool operator<(const basic_mmap<AccessMode, ByteT>& a,
          const basic_mmap<AccessMode, ByteT>& b);
  
  template<access_mode AccessMode, typename ByteT>
  bool operator<=(const basic_mmap<AccessMode, ByteT>& a,
          const basic_mmap<AccessMode, ByteT>& b);
  
  template<access_mode AccessMode, typename ByteT>
  bool operator>(const basic_mmap<AccessMode, ByteT>& a,
          const basic_mmap<AccessMode, ByteT>& b);
  
  template<access_mode AccessMode, typename ByteT>
  bool operator>=(const basic_mmap<AccessMode, ByteT>& a,
          const basic_mmap<AccessMode, ByteT>& b);
  
  /**
   * This is the basis for all read-only mmap objects and should be preferred over
   * directly using `basic_mmap`.
   */
  template<typename ByteT>
  using basic_mmap_source = basic_mmap<access_mode::read, ByteT>;
  
  /**
   * This is the basis for all read-write mmap objects and should be preferred over
   * directly using `basic_mmap`.
   */
  template<typename ByteT>
  using basic_mmap_sink = basic_mmap<access_mode::write, ByteT>;
  
  /**
   * These aliases cover the most common use cases, both representing a raw byte stream
   * (either with a char or an unsigned char/uint8_t).
   */
  using mmap_source = basic_mmap_source<char>;
  using ummap_source = basic_mmap_source<unsigned char>;
  
  using mmap_sink = basic_mmap_sink<char>;
  using ummap_sink = basic_mmap_sink<unsigned char>;
  
  /**
   * Convenience factory method that constructs a mapping for any `basic_mmap` or
   * `basic_mmap` type.
   */
  template<
      typename MMap,
      typename MappingToken
  > MMap make_mmap(const MappingToken& token,
          int64_t offset, int64_t length, std::error_code& error)
  {
      MMap mmap;
      mmap.map(token, offset, length, error);
      return mmap;
  }
  
  /**
   * Convenience factory method.
   *
   * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`,
   * `std::filesystem::path`, `std::vector<char>`, or similar), or a
   * `mmap_source::handle_type`.
   */
  template<typename MappingToken>
  mmap_source make_mmap_source(const MappingToken& token, mmap_source::size_type offset,
          mmap_source::size_type length, std::error_code& error)
  {
      return make_mmap<mmap_source>(token, offset, length, error);
  }
  
  template<typename MappingToken>
  mmap_source make_mmap_source(const MappingToken& token, std::error_code& error)
  {
      return make_mmap_source(token, 0, map_entire_file, error);
  }
  
  /**
   * Convenience factory method.
   *
   * MappingToken may be a String (`std::string`, `std::string_view`, `const char*`,
   * `std::filesystem::path`, `std::vector<char>`, or similar), or a
   * `mmap_sink::handle_type`.
   */
  template<typename MappingToken>
  mmap_sink make_mmap_sink(const MappingToken& token, mmap_sink::size_type offset,
          mmap_sink::size_type length, std::error_code& error)
  {
      return make_mmap<mmap_sink>(token, offset, length, error);
  }
  
  template<typename MappingToken>
  mmap_sink make_mmap_sink(const MappingToken& token, std::error_code& error)
  {
      return make_mmap_sink(token, 0, map_entire_file, error);
  }
  
  } // namespace mio
  
  // #include "detail/mmap.ipp"
  /* Copyright 2017 https://github.com/mandreyel
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy of this
   * software and associated documentation files (the "Software"), to deal in the Software
   * without restriction, including without limitation the rights to use, copy, modify,
   * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so, subject to the following
   * conditions:
   *
   * The above copyright notice and this permission notice shall be included in all copies
   * or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
   * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
   * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  
  #ifndef MIO_BASIC_MMAP_IMPL
  #define MIO_BASIC_MMAP_IMPL
  
  // #include "mio/mmap.hpp"
  
  // #include "mio/page.hpp"
  
  // #include "mio/detail/string_util.hpp"
  /* Copyright 2017 https://github.com/mandreyel
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy of this
   * software and associated documentation files (the "Software"), to deal in the Software
   * without restriction, including without limitation the rights to use, copy, modify,
   * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so, subject to the following
   * conditions:
   *
   * The above copyright notice and this permission notice shall be included in all copies
   * or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
   * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
   * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  
  #ifndef MIO_STRING_UTIL_HEADER
  #define MIO_STRING_UTIL_HEADER
  
  #include <type_traits>
  
  namespace mio {
  namespace detail {
  
  template<
      typename S,
      typename C = typename std::decay<S>::type,
      typename = decltype(std::declval<C>().data()),
      typename = typename std::enable_if<
          std::is_same<typename C::value_type, char>::value
  #ifdef _WIN32
          || std::is_same<typename C::value_type, wchar_t>::value
  #endif
      >::type
  > struct char_type_helper {
      using type = typename C::value_type;
  };
  
  template<class T>
  struct char_type {
      using type = typename char_type_helper<T>::type;
  };
  
  // TODO: can we avoid this brute force approach?
  template<>
  struct char_type<char*> {
      using type = char;
  };
  
  template<>
  struct char_type<const char*> {
      using type = char;
  };
  
  template<size_t N>
  struct char_type<char[N]> {
      using type = char;
  };
  
  template<size_t N>
  struct char_type<const char[N]> {
      using type = char;
  };
  
  #ifdef _WIN32
  template<>
  struct char_type<wchar_t*> {
      using type = wchar_t;
  };
  
  template<>
  struct char_type<const wchar_t*> {
      using type = wchar_t;
  };
  
  template<size_t N>
  struct char_type<wchar_t[N]> {
      using type = wchar_t;
  };
  
  template<size_t N>
  struct char_type<const wchar_t[N]> {
      using type = wchar_t;
  };
  #endif // _WIN32
  
  template<typename CharT, typename S>
  struct is_c_str_helper
  {
      static constexpr bool value = std::is_same<
          CharT*,
          // TODO: I'm so sorry for this... Can this be made cleaner?
          typename std::add_pointer<
              typename std::remove_cv<
                  typename std::remove_pointer<
                      typename std::decay<
                          S
                      >::type
                  >::type
              >::type
          >::type
      >::value;
  };
  
  template<typename S>
  struct is_c_str
  {
      static constexpr bool value = is_c_str_helper<char, S>::value;
  };
  
  #ifdef _WIN32
  template<typename S>
  struct is_c_wstr
  {
      static constexpr bool value = is_c_str_helper<wchar_t, S>::value;
  };
  #endif // _WIN32
  
  template<typename S>
  struct is_c_str_or_c_wstr
  {
      static constexpr bool value = is_c_str<S>::value
  #ifdef _WIN32
          || is_c_wstr<S>::value
  #endif
          ;
  };
  
  template<
      typename String,
      typename = decltype(std::declval<String>().data()),
      typename = typename std::enable_if<!is_c_str_or_c_wstr<String>::value>::type
  > const typename char_type<String>::type* c_str(const String& path)
  {
      return path.data();
  }
  
  template<
      typename String,
      typename = decltype(std::declval<String>().empty()),
      typename = typename std::enable_if<!is_c_str_or_c_wstr<String>::value>::type
  > bool empty(const String& path)
  {
      return path.empty();
  }
  
  template<
      typename String,
      typename = typename std::enable_if<is_c_str_or_c_wstr<String>::value>::type
  > const typename char_type<String>::type* c_str(String path)
  {
      return path;
  }
  
  template<
      typename String,
      typename = typename std::enable_if<is_c_str_or_c_wstr<String>::value>::type
  > bool empty(String path)
  {
      return !path || (*path == 0);
  }
  
  } // namespace detail
  } // namespace mio
  
  #endif // MIO_STRING_UTIL_HEADER
  
  
  #include <algorithm>
  
  #ifndef _WIN32
  # include <unistd.h>
  # include <fcntl.h>
  # include <sys/mman.h>
  # include <sys/stat.h>
  #endif
  
  namespace mio {
  namespace detail {
  
  #ifdef _WIN32
  namespace win {
  
  /** Returns the 4 upper bytes of an 8-byte integer. */
  inline DWORD int64_high(int64_t n) noexcept
  {
      return n >> 32;
  }
  
  /** Returns the 4 lower bytes of an 8-byte integer. */
  inline DWORD int64_low(int64_t n) noexcept
  {
      return n & 0xffffffff;
  }
  
  template<
      typename String,
      typename = typename std::enable_if<
          std::is_same<typename char_type<String>::type, char>::value
      >::type
  > file_handle_type open_file_helper(const String& path, const access_mode mode)
  {
      return ::CreateFileA(c_str(path),
              mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
              FILE_SHARE_READ | FILE_SHARE_WRITE,
              0,
              OPEN_EXISTING,
              FILE_ATTRIBUTE_NORMAL,
              0);
  }
  
  template<typename String>
  typename std::enable_if<
      std::is_same<typename char_type<String>::type, wchar_t>::value,
      file_handle_type
  >::type open_file_helper(const String& path, const access_mode mode)
  {
      return ::CreateFileW(c_str(path),
              mode == access_mode::read ? GENERIC_READ : GENERIC_READ | GENERIC_WRITE,
              FILE_SHARE_READ | FILE_SHARE_WRITE,
              0,
              OPEN_EXISTING,
              FILE_ATTRIBUTE_NORMAL,
              0);
  }
  
  } // win
  #endif // _WIN32
  
  /**
   * Returns the last platform specific system error (errno on POSIX and
   * GetLastError on Win) as a `std::error_code`.
   */
  inline std::error_code last_error() noexcept
  {
      std::error_code error;
  #ifdef _WIN32
      error.assign(GetLastError(), std::system_category());
  #else
      error.assign(errno, std::system_category());
  #endif
      return error;
  }
  
  template<typename String>
  file_handle_type open_file(const String& path, const access_mode mode,
          std::error_code& error)
  {
      error.clear();
      if(detail::empty(path))
      {
          error = std::make_error_code(std::errc::invalid_argument);
          return invalid_handle;
      }
  #ifdef _WIN32
      const auto handle = win::open_file_helper(path, mode);
  #else // POSIX
      const auto handle = ::open(c_str(path),
              mode == access_mode::read ? O_RDONLY : O_RDWR);
  #endif
      if(handle == invalid_handle)
      {
          error = detail::last_error();
      }
      return handle;
  }
  
  inline size_t query_file_size(file_handle_type handle, std::error_code& error)
  {
      error.clear();
  #ifdef _WIN32
      LARGE_INTEGER file_size;
      if(::GetFileSizeEx(handle, &file_size) == 0)
      {
          error = detail::last_error();
          return 0;
      }
  	return static_cast<int64_t>(file_size.QuadPart);
  #else // POSIX
      struct stat sbuf;
      if(::fstat(handle, &sbuf) == -1)
      {
          error = detail::last_error();
          return 0;
      }
      return sbuf.st_size;
  #endif
  }
  
  struct mmap_context
  {
      char* data;
      int64_t length;
      int64_t mapped_length;
  #ifdef _WIN32
      file_handle_type file_mapping_handle;
  #endif
  };
  
  inline mmap_context memory_map(const file_handle_type file_handle, const int64_t offset,
      const int64_t length, const access_mode mode, std::error_code& error)
  {
      const int64_t aligned_offset = make_offset_page_aligned(offset);
      const int64_t length_to_map = offset - aligned_offset + length;
  #ifdef _WIN32
      const int64_t max_file_size = offset + length;
      const auto file_mapping_handle = ::CreateFileMapping(
              file_handle,
              0,
              mode == access_mode::read ? PAGE_READONLY : PAGE_READWRITE,
              win::int64_high(max_file_size),
              win::int64_low(max_file_size),
              0);
      if(file_mapping_handle == invalid_handle)
      {
          error = detail::last_error();
          return {};
      }
      char* mapping_start = static_cast<char*>(::MapViewOfFile(
              file_mapping_handle,
              mode == access_mode::read ? FILE_MAP_READ : FILE_MAP_WRITE,
              win::int64_high(aligned_offset),
              win::int64_low(aligned_offset),
              length_to_map));
      if(mapping_start == nullptr)
      {
          // Close file handle if mapping it failed.
          ::CloseHandle(file_mapping_handle);
          error = detail::last_error();
          return {};
      }
  #else // POSIX
      char* mapping_start = static_cast<char*>(::mmap(
              0, // Don't give hint as to where to map.
              length_to_map,
              mode == access_mode::read ? PROT_READ : PROT_WRITE,
              MAP_SHARED,
              file_handle,
              aligned_offset));
      if(mapping_start == MAP_FAILED)
      {
          error = detail::last_error();
          return {};
      }
  #endif
      mmap_context ctx;
      ctx.data = mapping_start + offset - aligned_offset;
      ctx.length = length;
      ctx.mapped_length = length_to_map;
  #ifdef _WIN32
      ctx.file_mapping_handle = file_mapping_handle;
  #endif
      return ctx;
  }
  
  } // namespace detail
  
  // -- basic_mmap --
  
  template<access_mode AccessMode, typename ByteT>
  basic_mmap<AccessMode, ByteT>::~basic_mmap()
  {
      conditional_sync();
      unmap();
  }
  
  template<access_mode AccessMode, typename ByteT>
  basic_mmap<AccessMode, ByteT>::basic_mmap(basic_mmap&& other)
      : data_(std::move(other.data_))
      , length_(std::move(other.length_))
      , mapped_length_(std::move(other.mapped_length_))
      , file_handle_(std::move(other.file_handle_))
  #ifdef _WIN32
      , file_mapping_handle_(std::move(other.file_mapping_handle_))
  #endif
      , is_handle_internal_(std::move(other.is_handle_internal_))
  {
      other.data_ = nullptr;
      other.length_ = other.mapped_length_ = 0;
      other.file_handle_ = invalid_handle;
  #ifdef _WIN32
      other.file_mapping_handle_ = invalid_handle;
  #endif
  }
  
  template<access_mode AccessMode, typename ByteT>
  basic_mmap<AccessMode, ByteT>&
  basic_mmap<AccessMode, ByteT>::operator=(basic_mmap&& other)
  {
      if(this != &other)
      {
          // First the existing mapping needs to be removed.
          unmap();
          data_ = std::move(other.data_);
          length_ = std::move(other.length_);
          mapped_length_ = std::move(other.mapped_length_);
          file_handle_ = std::move(other.file_handle_);
  #ifdef _WIN32
          file_mapping_handle_ = std::move(other.file_mapping_handle_);
  #endif
          is_handle_internal_ = std::move(other.is_handle_internal_);
  
          // The moved from basic_mmap's fields need to be reset, because
          // otherwise other's destructor will unmap the same mapping that was
          // just moved into this.
          other.data_ = nullptr;
          other.length_ = other.mapped_length_ = 0;
          other.file_handle_ = invalid_handle;
  #ifdef _WIN32
          other.file_mapping_handle_ = invalid_handle;
  #endif
          other.is_handle_internal_ = false;
      }
      return *this;
  }
  
  template<access_mode AccessMode, typename ByteT>
  typename basic_mmap<AccessMode, ByteT>::handle_type
  basic_mmap<AccessMode, ByteT>::mapping_handle() const noexcept
  {
  #ifdef _WIN32
      return file_mapping_handle_;
  #else
      return file_handle_;
  #endif
  }
  
  template<access_mode AccessMode, typename ByteT>
  template<typename String>
  void basic_mmap<AccessMode, ByteT>::map(const String& path, const size_type offset,
          const size_type length, std::error_code& error)
  {
      error.clear();
      if(detail::empty(path))
      {
          error = std::make_error_code(std::errc::invalid_argument);
          return;
      }
      const auto handle = detail::open_file(path, AccessMode, error);
      if(error)
      {
          return;
      }
  
      map(handle, offset, length, error);
      // This MUST be after the call to map, as that sets this to true.
      if(!error)
      {
          is_handle_internal_ = true;
      }
  }
  
  template<access_mode AccessMode, typename ByteT>
  void basic_mmap<AccessMode, ByteT>::map(const handle_type handle,
          const size_type offset, const size_type length, std::error_code& error)
  {
      error.clear();
      if(handle == invalid_handle)
      {
          error = std::make_error_code(std::errc::bad_file_descriptor);
          return;
      }
  
      const auto file_size = detail::query_file_size(handle, error);
      if(error)
      {
          return;
      }
  
      if(offset + length > file_size)
      {
          error = std::make_error_code(std::errc::invalid_argument);
          return;
      }
  
      const auto ctx = detail::memory_map(handle, offset,
              length == map_entire_file ? (file_size - offset) : length,
              AccessMode, error);
      if(!error)
      {
          // We must unmap the previous mapping that may have existed prior to this call.
          // Note that this must only be invoked after a new mapping has been created in
          // order to provide the strong guarantee that, should the new mapping fail, the
          // `map` function leaves this instance in a state as though the function had
          // never been invoked.
          unmap();
          file_handle_ = handle;
          is_handle_internal_ = false;
          data_ = reinterpret_cast<pointer>(ctx.data);
          length_ = ctx.length;
          mapped_length_ = ctx.mapped_length;
  #ifdef _WIN32
          file_mapping_handle_ = ctx.file_mapping_handle;
  #endif
      }
  }
  
  template<access_mode AccessMode, typename ByteT>
  template<access_mode A>
  typename std::enable_if<A == access_mode::write, void>::type
  basic_mmap<AccessMode, ByteT>::sync(std::error_code& error)
  {
      error.clear();
      if(!is_open())
      {
          error = std::make_error_code(std::errc::bad_file_descriptor);
          return;
      }
  
      if(data())
      {
  #ifdef _WIN32
          if(::FlushViewOfFile(get_mapping_start(), mapped_length_) == 0
             || ::FlushFileBuffers(file_handle_) == 0)
  #else // POSIX
          if(::msync(get_mapping_start(), mapped_length_, MS_SYNC) != 0)
  #endif
          {
              error = detail::last_error();
              return;
          }
      }
  #ifdef _WIN32
      if(::FlushFileBuffers(file_handle_) == 0)
      {
          error = detail::last_error();
      }
  #endif
  }
  
  template<access_mode AccessMode, typename ByteT>
  void basic_mmap<AccessMode, ByteT>::unmap()
  {
      if(!is_open()) { return; }
      // TODO do we care about errors here?
  #ifdef _WIN32
      if(is_mapped())
      {
          ::UnmapViewOfFile(get_mapping_start());
          ::CloseHandle(file_mapping_handle_);
      }
  #else // POSIX
      if(data_) { ::munmap(const_cast<pointer>(get_mapping_start()), mapped_length_); }
  #endif
  
      // If `file_handle_` was obtained by our opening it (when map is called with
      // a path, rather than an existing file handle), we need to close it,
      // otherwise it must not be closed as it may still be used outside this
      // instance.
      if(is_handle_internal_)
      {
  #ifdef _WIN32
          ::CloseHandle(file_handle_);
  #else // POSIX
          ::close(file_handle_);
  #endif
      }
  
      // Reset fields to their default values.
      data_ = nullptr;
      length_ = mapped_length_ = 0;
      file_handle_ = invalid_handle;
  #ifdef _WIN32
      file_mapping_handle_ = invalid_handle;
  #endif
  }
  
  template<access_mode AccessMode, typename ByteT>
  bool basic_mmap<AccessMode, ByteT>::is_mapped() const noexcept
  {
  #ifdef _WIN32
      return file_mapping_handle_ != invalid_handle;
  #else // POSIX
      return is_open();
  #endif
  }
  
  template<access_mode AccessMode, typename ByteT>
  void basic_mmap<AccessMode, ByteT>::swap(basic_mmap& other)
  {
      if(this != &other)
      {
          using std::swap;
          swap(data_, other.data_);
          swap(file_handle_, other.file_handle_);
  #ifdef _WIN32
          swap(file_mapping_handle_, other.file_mapping_handle_);
  #endif
          swap(length_, other.length_);
          swap(mapped_length_, other.mapped_length_);
          swap(is_handle_internal_, other.is_handle_internal_);
      }
  }
  
  template<access_mode AccessMode, typename ByteT>
  template<access_mode A>
  typename std::enable_if<A == access_mode::write, void>::type
  basic_mmap<AccessMode, ByteT>::conditional_sync()
  {
      // This is invoked from the destructor, so not much we can do about
      // failures here.
      std::error_code ec;
      sync(ec);
  }
  
  template<access_mode AccessMode, typename ByteT>
  template<access_mode A>
  typename std::enable_if<A == access_mode::read, void>::type
  basic_mmap<AccessMode, ByteT>::conditional_sync()
  {
      // noop
  }
  
  template<access_mode AccessMode, typename ByteT>
  bool operator==(const basic_mmap<AccessMode, ByteT>& a,
          const basic_mmap<AccessMode, ByteT>& b)
  {
      return a.data() == b.data()
          && a.size() == b.size();
  }
  
  template<access_mode AccessMode, typename ByteT>
  bool operator!=(const basic_mmap<AccessMode, ByteT>& a,
          const basic_mmap<AccessMode, ByteT>& b)
  {
      return !(a == b);
  }
  
  template<access_mode AccessMode, typename ByteT>
  bool operator<(const basic_mmap<AccessMode, ByteT>& a,
          const basic_mmap<AccessMode, ByteT>& b)
  {
      if(a.data() == b.data()) { return a.size() < b.size(); }
      return a.data() < b.data();
  }
  
  template<access_mode AccessMode, typename ByteT>
  bool operator<=(const basic_mmap<AccessMode, ByteT>& a,
          const basic_mmap<AccessMode, ByteT>& b)
  {
      return !(a > b);
  }
  
  template<access_mode AccessMode, typename ByteT>
  bool operator>(const basic_mmap<AccessMode, ByteT>& a,
          const basic_mmap<AccessMode, ByteT>& b)
  {
      if(a.data() == b.data()) { return a.size() > b.size(); }
      return a.data() > b.data();
  }
  
  template<access_mode AccessMode, typename ByteT>
  bool operator>=(const basic_mmap<AccessMode, ByteT>& a,
          const basic_mmap<AccessMode, ByteT>& b)
  {
      return !(a < b);
  }
  
  } // namespace mio
  
  #endif // MIO_BASIC_MMAP_IMPL
  
  
  #endif // MIO_MMAP_HEADER
  /* Copyright 2017 https://github.com/mandreyel
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy of this
   * software and associated documentation files (the "Software"), to deal in the Software
   * without restriction, including without limitation the rights to use, copy, modify,
   * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so, subject to the following
   * conditions:
   *
   * The above copyright notice and this permission notice shall be included in all copies
   * or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
   * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
   * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  
  #ifndef MIO_PAGE_HEADER
  #define MIO_PAGE_HEADER
  
  #ifdef _WIN32
  # include <windows.h>
  #else
  # include <unistd.h>
  #endif
  
  namespace mio {
  
  /**
   * This is used by `basic_mmap` to determine whether to create a read-only or
   * a read-write memory mapping.
   */
  enum class access_mode
  {
      read,
      write
  };
  
  /**
   * Determines the operating system's page allocation granularity.
   *
   * On the first call to this function, it invokes the operating system specific syscall
   * to determine the page size, caches the value, and returns it. Any subsequent call to
   * this function serves the cached value, so no further syscalls are made.
   */
  inline size_t page_size()
  {
      static const size_t page_size = []
      {
  #ifdef _WIN32
          SYSTEM_INFO SystemInfo;
          GetSystemInfo(&SystemInfo);
          return SystemInfo.dwAllocationGranularity;
  #else
          return sysconf(_SC_PAGE_SIZE);
  #endif
      }();
      return page_size;
  }
  
  /**
   * Alligns `offset` to the operating's system page size such that it subtracts the
   * difference until the nearest page boundary before `offset`, or does nothing if
   * `offset` is already page aligned.
   */
  inline size_t make_offset_page_aligned(size_t offset) noexcept
  {
      const size_t page_size_ = page_size();
      // Use integer division to round down to the nearest page alignment.
      return offset / page_size_ * page_size_;
  }
  
  } // namespace mio
  
  #endif // MIO_PAGE_HEADER
  /* Copyright 2017 https://github.com/mandreyel
   *
   * Permission is hereby granted, free of charge, to any person obtaining a copy of this
   * software and associated documentation files (the "Software"), to deal in the Software
   * without restriction, including without limitation the rights to use, copy, modify,
   * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
   * permit persons to whom the Software is furnished to do so, subject to the following
   * conditions:
   *
   * The above copyright notice and this permission notice shall be included in all copies
   * or substantial portions of the Software.
   *
   * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
   * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
   * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
   * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
   * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE
   * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
   */
  
  #ifndef MIO_SHARED_MMAP_HEADER
  #define MIO_SHARED_MMAP_HEADER
  
  // #include "mio/mmap.hpp"
  
  
  #include <system_error> // std::error_code
  #include <memory> // std::shared_ptr
  
  namespace mio {
  
  /**
   * Exposes (nearly) the same interface as `basic_mmap`, but endowes it with
   * `std::shared_ptr` semantics.
   *
   * This is not the default behaviour of `basic_mmap` to avoid allocating on the heap if
   * shared semantics are not required.
   */
  template<
      access_mode AccessMode,
      typename ByteT
  > class basic_shared_mmap
  {
      using impl_type = basic_mmap<AccessMode, ByteT>;
      std::shared_ptr<impl_type> pimpl_;
  
  public:
      using value_type = typename impl_type::value_type;
      using size_type = typename impl_type::size_type;
      using reference = typename impl_type::reference;
      using const_reference = typename impl_type::const_reference;
      using pointer = typename impl_type::pointer;
      using const_pointer = typename impl_type::const_pointer;
      using difference_type = typename impl_type::difference_type;
      using iterator = typename impl_type::iterator;
      using const_iterator = typename impl_type::const_iterator;
      using reverse_iterator = typename impl_type::reverse_iterator;
      using const_reverse_iterator = typename impl_type::const_reverse_iterator;
      using iterator_category = typename impl_type::iterator_category;
      using handle_type = typename impl_type::handle_type;
      using mmap_type = impl_type;
  
      basic_shared_mmap() = default;
      basic_shared_mmap(const basic_shared_mmap&) = default;
      basic_shared_mmap& operator=(const basic_shared_mmap&) = default;
      basic_shared_mmap(basic_shared_mmap&&) = default;
      basic_shared_mmap& operator=(basic_shared_mmap&&) = default;
  
      /** Takes ownership of an existing mmap object. */
      basic_shared_mmap(mmap_type&& mmap)
          : pimpl_(std::make_shared<mmap_type>(std::move(mmap)))
      {}
  
      /** Takes ownership of an existing mmap object. */
      basic_shared_mmap& operator=(mmap_type&& mmap)
      {
          pimpl_ = std::make_shared<mmap_type>(std::move(mmap));
          return *this;
      }
  
      /** Initializes this object with an already established shared mmap. */
      basic_shared_mmap(std::shared_ptr<mmap_type> mmap) : pimpl_(std::move(mmap)) {}
  
      /** Initializes this object with an already established shared mmap. */
      basic_shared_mmap& operator=(std::shared_ptr<mmap_type> mmap)
      {
          pimpl_ = std::move(mmap);
          return *this;
      }
  
  #ifdef __cpp_exceptions
      /**
       * The same as invoking the `map` function, except any error that may occur
       * while establishing the mapping is wrapped in a `std::system_error` and is
       * thrown.
       */
      template<typename String>
      basic_shared_mmap(const String& path, const size_type offset = 0, const size_type length = map_entire_file)
      {
          std::error_code error;
          map(path, offset, length, error);
          if(error) { throw std::system_error(error); }
      }
  
      /**
       * The same as invoking the `map` function, except any error that may occur
       * while establishing the mapping is wrapped in a `std::system_error` and is
       * thrown.
       */
      basic_shared_mmap(const handle_type handle, const size_type offset = 0, const size_type length = map_entire_file)
      {
          std::error_code error;
          map(handle, offset, length, error);
          if(error) { throw std::system_error(error); }
      }
  #endif // __cpp_exceptions
  
      /**
       * If this is a read-write mapping and the last reference to the mapping,
       * the destructor invokes sync. Regardless of the access mode, unmap is
       * invoked as a final step.
       */
      ~basic_shared_mmap() = default;
  
      /** Returns the underlying `std::shared_ptr` instance that holds the mmap. */
      std::shared_ptr<mmap_type> get_shared_ptr() { return pimpl_; }
  
      /**
       * On UNIX systems 'file_handle' and 'mapping_handle' are the same. On Windows,
       * however, a mapped region of a file gets its own handle, which is returned by
       * 'mapping_handle'.
       */
      handle_type file_handle() const noexcept
      {
          return pimpl_ ? pimpl_->file_handle() : invalid_handle;
      }
  
      handle_type mapping_handle() const noexcept
      {
          return pimpl_ ? pimpl_->mapping_handle() : invalid_handle;
      }
  
      /** Returns whether a valid memory mapping has been created. */
      bool is_open() const noexcept { return pimpl_ && pimpl_->is_open(); }
  
      /**
       * Returns true if no mapping was established, that is, conceptually the
       * same as though the length that was mapped was 0. This function is
       * provided so that this class has Container semantics.
       */
      bool empty() const noexcept { return !pimpl_ || pimpl_->empty(); }
  
      /**
       * `size` and `length` both return the logical length, i.e. the number of bytes
       * user requested to be mapped, while `mapped_length` returns the actual number of
       * bytes that were mapped which is a multiple of the underlying operating system's
       * page allocation granularity.
       */
      size_type size() const noexcept { return pimpl_ ? pimpl_->length() : 0; }
      size_type length() const noexcept { return pimpl_ ? pimpl_->length() : 0; }
      size_type mapped_length() const noexcept
      {
          return pimpl_ ? pimpl_->mapped_length() : 0;
      }
  
      /**
       * Returns a pointer to the first requested byte, or `nullptr` if no memory mapping
       * exists.
       */
      template<
          access_mode A = AccessMode,
          typename = typename std::enable_if<A == access_mode::write>::type
      > pointer data() noexcept { return pimpl_->data(); }
      const_pointer data() const noexcept { return pimpl_ ? pimpl_->data() : nullptr; }
  
      /**
       * Returns an iterator to the first requested byte, if a valid memory mapping
       * exists, otherwise this function call is undefined behaviour.
       */
      iterator begin() noexcept { return pimpl_->begin(); }
      const_iterator begin() const noexcept { return pimpl_->begin(); }
      const_iterator cbegin() const noexcept { return pimpl_->cbegin(); }
  
      /**
       * Returns an iterator one past the last requested byte, if a valid memory mapping
       * exists, otherwise this function call is undefined behaviour.
       */
      template<
          access_mode A = AccessMode,
          typename = typename std::enable_if<A == access_mode::write>::type
      > iterator end() noexcept { return pimpl_->end(); }
      const_iterator end() const noexcept { return pimpl_->end(); }
      const_iterator cend() const noexcept { return pimpl_->cend(); }
  
      /**
       * Returns a reverse iterator to the last memory mapped byte, if a valid
       * memory mapping exists, otherwise this function call is undefined
       * behaviour.
       */
      template<
          access_mode A = AccessMode,
          typename = typename std::enable_if<A == access_mode::write>::type
      > reverse_iterator rbegin() noexcept { return pimpl_->rbegin(); }
      const_reverse_iterator rbegin() const noexcept { return pimpl_->rbegin(); }
      const_reverse_iterator crbegin() const noexcept { return pimpl_->crbegin(); }
  
      /**
       * Returns a reverse iterator past the first mapped byte, if a valid memory
       * mapping exists, otherwise this function call is undefined behaviour.
       */
      template<
          access_mode A = AccessMode,
          typename = typename std::enable_if<A == access_mode::write>::type
      > reverse_iterator rend() noexcept { return pimpl_->rend(); }
      const_reverse_iterator rend() const noexcept { return pimpl_->rend(); }
      const_reverse_iterator crend() const noexcept { return pimpl_->crend(); }
  
      /**
       * Returns a reference to the `i`th byte from the first requested byte (as returned
       * by `data`). If this is invoked when no valid memory mapping has been created
       * prior to this call, undefined behaviour ensues.
       */
      reference operator[](const size_type i) noexcept { return (*pimpl_)[i]; }
      const_reference operator[](const size_type i) const noexcept { return (*pimpl_)[i]; }
  
      /**
       * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
       * reason is reported via `error` and the object remains in a state as if this
       * function hadn't been called.
       *
       * `path`, which must be a path to an existing file, is used to retrieve a file
       * handle (which is closed when the object destructs or `unmap` is called), which is
       * then used to memory map the requested region. Upon failure, `error` is set to
       * indicate the reason and the object remains in an unmapped state.
       *
       * `offset` is the number of bytes, relative to the start of the file, where the
       * mapping should begin. When specifying it, there is no need to worry about
       * providing a value that is aligned with the operating system's page allocation
       * granularity. This is adjusted by the implementation such that the first requested
       * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
       * `offset` from the start of the file.
       *
       * `length` is the number of bytes to map. It may be `map_entire_file`, in which
       * case a mapping of the entire file is created.
       */
      template<typename String>
      void map(const String& path, const size_type offset,
          const size_type length, std::error_code& error)
      {
          map_impl(path, offset, length, error);
      }
  
      /**
       * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
       * reason is reported via `error` and the object remains in a state as if this
       * function hadn't been called.
       *
       * `path`, which must be a path to an existing file, is used to retrieve a file
       * handle (which is closed when the object destructs or `unmap` is called), which is
       * then used to memory map the requested region. Upon failure, `error` is set to
       * indicate the reason and the object remains in an unmapped state.
       *
       * The entire file is mapped.
       */
      template<typename String>
      void map(const String& path, std::error_code& error)
      {
          map_impl(path, 0, map_entire_file, error);
      }
  
      /**
       * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
       * reason is reported via `error` and the object remains in a state as if this
       * function hadn't been called.
       *
       * `handle`, which must be a valid file handle, which is used to memory map the
       * requested region. Upon failure, `error` is set to indicate the reason and the
       * object remains in an unmapped state.
       *
       * `offset` is the number of bytes, relative to the start of the file, where the
       * mapping should begin. When specifying it, there is no need to worry about
       * providing a value that is aligned with the operating system's page allocation
       * granularity. This is adjusted by the implementation such that the first requested
       * byte (as returned by `data` or `begin`), so long as `offset` is valid, will be at
       * `offset` from the start of the file.
       *
       * `length` is the number of bytes to map. It may be `map_entire_file`, in which
       * case a mapping of the entire file is created.
       */
      void map(const handle_type handle, const size_type offset,
          const size_type length, std::error_code& error)
      {
          map_impl(handle, offset, length, error);
      }
  
      /**
       * Establishes a memory mapping with AccessMode. If the mapping is unsuccesful, the
       * reason is reported via `error` and the object remains in a state as if this
       * function hadn't been called.
       *
       * `handle`, which must be a valid file handle, which is used to memory map the
       * requested region. Upon failure, `error` is set to indicate the reason and the
       * object remains in an unmapped state.
       *
       * The entire file is mapped.
       */
      void map(const handle_type handle, std::error_code& error)
      {
          map_impl(handle, 0, map_entire_file, error);
      }
  
      /**
       * If a valid memory mapping has been created prior to this call, this call
       * instructs the kernel to unmap the memory region and disassociate this object
       * from the file.
       *
       * The file handle associated with the file that is mapped is only closed if the
       * mapping was created using a file path. If, on the other hand, an existing
       * file handle was used to create the mapping, the file handle is not closed.
       */
      void unmap() { if(pimpl_) pimpl_->unmap(); }
  
      void swap(basic_shared_mmap& other) { pimpl_.swap(other.pimpl_); }
  
      /** Flushes the memory mapped page to disk. Errors are reported via `error`. */
      template<
          access_mode A = AccessMode,
          typename = typename std::enable_if<A == access_mode::write>::type
      > void sync(std::error_code& error) { if(pimpl_) pimpl_->sync(error); }
  
      /** All operators compare the underlying `basic_mmap`'s addresses. */
  
      friend bool operator==(const basic_shared_mmap& a, const basic_shared_mmap& b)
      {
          return a.pimpl_ == b.pimpl_;
      }
  
      friend bool operator!=(const basic_shared_mmap& a, const basic_shared_mmap& b)
      {
          return !(a == b);
      }
  
      friend bool operator<(const basic_shared_mmap& a, const basic_shared_mmap& b)
      {
          return a.pimpl_ < b.pimpl_;
      }
  
      friend bool operator<=(const basic_shared_mmap& a, const basic_shared_mmap& b)
      {
          return a.pimpl_ <= b.pimpl_;
      }
  
      friend bool operator>(const basic_shared_mmap& a, const basic_shared_mmap& b)
      {
          return a.pimpl_ > b.pimpl_;
      }
  
      friend bool operator>=(const basic_shared_mmap& a, const basic_shared_mmap& b)
      {
          return a.pimpl_ >= b.pimpl_;
      }
  
  private:
      template<typename MappingToken>
      void map_impl(const MappingToken& token, const size_type offset,
          const size_type length, std::error_code& error)
      {
          if(!pimpl_)
          {
              mmap_type mmap = make_mmap<mmap_type>(token, offset, length, error);
              if(error) { return; }
              pimpl_ = std::make_shared<mmap_type>(std::move(mmap));
          }
          else
          {
              pimpl_->map(token, offset, length, error);
          }
      }
  };
  
  /**
   * This is the basis for all read-only mmap objects and should be preferred over
   * directly using basic_shared_mmap.
   */
  template<typename ByteT>
  using basic_shared_mmap_source = basic_shared_mmap<access_mode::read, ByteT>;
  
  /**
   * This is the basis for all read-write mmap objects and should be preferred over
   * directly using basic_shared_mmap.
   */
  template<typename ByteT>
  using basic_shared_mmap_sink = basic_shared_mmap<access_mode::write, ByteT>;
  
  /**
   * These aliases cover the most common use cases, both representing a raw byte stream
   * (either with a char or an unsigned char/uint8_t).
   */
  using shared_mmap_source = basic_shared_mmap_source<char>;
  using shared_ummap_source = basic_shared_mmap_source<unsigned char>;
  
  using shared_mmap_sink = basic_shared_mmap_sink<char>;
  using shared_ummap_sink = basic_shared_mmap_sink<unsigned char>;
  
  } // namespace mio
  
  #endif // MIO_SHARED_MMAP_HEADER
  
  /** @file
   *  @brief Contains the main CSV parsing algorithm and various utility functions
   */
  
  #include <algorithm>
  #include <array>
  #include <condition_variable>
  #include <deque>
  #include <fstream>
  #include <memory>
  #include <mutex>
  #include <unordered_map>
  #include <unordered_set>
  #include <thread>
  #include <vector>
  
  #include <memory>
  #include <unordered_map>
  #include <string>
  #include <vector>
  
  /** @file
   *  A standalone header file containing shared code
   */
  
  #include <algorithm>
  #include <array>
  #include <cmath>
  #include <cstdlib>
  #include <deque>
  
  #if defined(_WIN32)
  #include <Windows.h>
  #define WIN32_LEAN_AND_MEAN
  #undef max
  #undef min
  #elif defined(__linux__)
  #include <unistd.h>
  #endif
  
   /** Helper macro which should be #defined as "inline"
    *  in the single header version
    */
  #define CSV_INLINE inline
  
  #include <type_traits>
  
  // Copyright 2017-2019 by Martin Moene
  //
  // string-view lite, a C++17-like string_view for C++98 and later.
  // For more information see https://github.com/martinmoene/string-view-lite
  //
  // Distributed under the Boost Software License, Version 1.0.
  // (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  
  
  #ifndef NONSTD_SV_LITE_H_INCLUDED
  #define NONSTD_SV_LITE_H_INCLUDED
  
  #define string_view_lite_MAJOR  1
  #define string_view_lite_MINOR  1
  #define string_view_lite_PATCH  0
  
  #define string_view_lite_VERSION  nssv_STRINGIFY(string_view_lite_MAJOR) "." nssv_STRINGIFY(string_view_lite_MINOR) "." nssv_STRINGIFY(string_view_lite_PATCH)
  
  #define nssv_STRINGIFY(  x )  nssv_STRINGIFY_( x )
  #define nssv_STRINGIFY_( x )  #x
  
  // string-view lite configuration:
  
  #define nssv_STRING_VIEW_DEFAULT  0
  #define nssv_STRING_VIEW_NONSTD   1
  #define nssv_STRING_VIEW_STD      2
  
  #if !defined( nssv_CONFIG_SELECT_STRING_VIEW )
  # define nssv_CONFIG_SELECT_STRING_VIEW  ( nssv_HAVE_STD_STRING_VIEW ? nssv_STRING_VIEW_STD : nssv_STRING_VIEW_NONSTD )
  #endif
  
  #if defined( nssv_CONFIG_SELECT_STD_STRING_VIEW ) || defined( nssv_CONFIG_SELECT_NONSTD_STRING_VIEW )
  # error nssv_CONFIG_SELECT_STD_STRING_VIEW and nssv_CONFIG_SELECT_NONSTD_STRING_VIEW are deprecated and removed, please use nssv_CONFIG_SELECT_STRING_VIEW=nssv_STRING_VIEW_...
  #endif
  
  #ifndef  nssv_CONFIG_STD_SV_OPERATOR
  # define nssv_CONFIG_STD_SV_OPERATOR  0
  #endif
  
  #ifndef  nssv_CONFIG_USR_SV_OPERATOR
  # define nssv_CONFIG_USR_SV_OPERATOR  1
  #endif
  
  #ifdef   nssv_CONFIG_CONVERSION_STD_STRING
  # define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS   nssv_CONFIG_CONVERSION_STD_STRING
  # define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS  nssv_CONFIG_CONVERSION_STD_STRING
  #endif
  
  #ifndef  nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
  # define nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS  1
  #endif
  
  #ifndef  nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
  # define nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS  1
  #endif
  
  // Control presence of exception handling (try and auto discover):
  
  #ifndef nssv_CONFIG_NO_EXCEPTIONS
  # if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND)
  #  define nssv_CONFIG_NO_EXCEPTIONS  0
  # else
  #  define nssv_CONFIG_NO_EXCEPTIONS  1
  # endif
  #endif
  
  // C++ language version detection (C++20 is speculative):
  // Note: VC14.0/1900 (VS2015) lacks too much from C++14.
  
  #ifndef   nssv_CPLUSPLUS
  # if defined(_MSVC_LANG ) && !defined(__clang__)
  #  define nssv_CPLUSPLUS  (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
  # else
  #  define nssv_CPLUSPLUS  __cplusplus
  # endif
  #endif
  
  #define nssv_CPP98_OR_GREATER  ( nssv_CPLUSPLUS >= 199711L )
  #define nssv_CPP11_OR_GREATER  ( nssv_CPLUSPLUS >= 201103L )
  #define nssv_CPP11_OR_GREATER_ ( nssv_CPLUSPLUS >= 201103L )
  #define nssv_CPP14_OR_GREATER  ( nssv_CPLUSPLUS >= 201402L )
  #define nssv_CPP17_OR_GREATER  ( nssv_CPLUSPLUS >= 201703L )
  #define nssv_CPP20_OR_GREATER  ( nssv_CPLUSPLUS >= 202000L )
  
  // use C++17 std::string_view if available and requested:
  
  #if nssv_CPP17_OR_GREATER && defined(__has_include )
  # if __has_include( <string_view> )
  #  define nssv_HAVE_STD_STRING_VIEW  1
  # else
  #  define nssv_HAVE_STD_STRING_VIEW  0
  # endif
  #else
  # define  nssv_HAVE_STD_STRING_VIEW  0
  #endif
  
  #define  nssv_USES_STD_STRING_VIEW  ( (nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_STD) || ((nssv_CONFIG_SELECT_STRING_VIEW == nssv_STRING_VIEW_DEFAULT) && nssv_HAVE_STD_STRING_VIEW) )
  
  #define nssv_HAVE_STARTS_WITH ( nssv_CPP20_OR_GREATER || !nssv_USES_STD_STRING_VIEW )
  #define nssv_HAVE_ENDS_WITH     nssv_HAVE_STARTS_WITH
  
  //
  // Use C++17 std::string_view:
  //
  
  #if nssv_USES_STD_STRING_VIEW
  
  #include <string_view>
  
  // Extensions for std::string:
  
  #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
  
  namespace nonstd {
  
  template< class CharT, class Traits, class Allocator = std::allocator<CharT> >
  std::basic_string<CharT, Traits, Allocator>
  to_string( std::basic_string_view<CharT, Traits> v, Allocator const & a = Allocator() )
  {
      return std::basic_string<CharT,Traits, Allocator>( v.begin(), v.end(), a );
  }
  
  template< class CharT, class Traits, class Allocator >
  std::basic_string_view<CharT, Traits>
  to_string_view( std::basic_string<CharT, Traits, Allocator> const & s )
  {
      return std::basic_string_view<CharT, Traits>( s.data(), s.size() );
  }
  
  // Literal operators sv and _sv:
  
  #if nssv_CONFIG_STD_SV_OPERATOR
  
  using namespace std::literals::string_view_literals;
  
  #endif
  
  #if nssv_CONFIG_USR_SV_OPERATOR
  
  inline namespace literals {
  inline namespace string_view_literals {
  
  
  constexpr std::string_view operator "" _sv( const char* str, size_t len ) noexcept  // (1)
  {
      return std::string_view{ str, len };
  }
  
  constexpr std::u16string_view operator "" _sv( const char16_t* str, size_t len ) noexcept  // (2)
  {
      return std::u16string_view{ str, len };
  }
  
  constexpr std::u32string_view operator "" _sv( const char32_t* str, size_t len ) noexcept  // (3)
  {
      return std::u32string_view{ str, len };
  }
  
  constexpr std::wstring_view operator "" _sv( const wchar_t* str, size_t len ) noexcept  // (4)
  {
      return std::wstring_view{ str, len };
  }
  
  }} // namespace literals::string_view_literals
  
  #endif // nssv_CONFIG_USR_SV_OPERATOR
  
  } // namespace nonstd
  
  #endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
  
  namespace nonstd {
  
  using std::string_view;
  using std::wstring_view;
  using std::u16string_view;
  using std::u32string_view;
  using std::basic_string_view;
  
  // literal "sv" and "_sv", see above
  
  using std::operator==;
  using std::operator!=;
  using std::operator<;
  using std::operator<=;
  using std::operator>;
  using std::operator>=;
  
  using std::operator<<;
  
  } // namespace nonstd
  
  #else // nssv_HAVE_STD_STRING_VIEW
  
  //
  // Before C++17: use string_view lite:
  //
  
  // Compiler versions:
  //
  // MSVC++ 6.0  _MSC_VER == 1200 (Visual Studio 6.0)
  // MSVC++ 7.0  _MSC_VER == 1300 (Visual Studio .NET 2002)
  // MSVC++ 7.1  _MSC_VER == 1310 (Visual Studio .NET 2003)
  // MSVC++ 8.0  _MSC_VER == 1400 (Visual Studio 2005)
  // MSVC++ 9.0  _MSC_VER == 1500 (Visual Studio 2008)
  // MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010)
  // MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012)
  // MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013)
  // MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015)
  // MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017)
  
  #if defined(_MSC_VER ) && !defined(__clang__)
  # define nssv_COMPILER_MSVC_VER      (_MSC_VER )
  # define nssv_COMPILER_MSVC_VERSION  (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900 ) ) )
  #else
  # define nssv_COMPILER_MSVC_VER      0
  # define nssv_COMPILER_MSVC_VERSION  0
  #endif
  
  #define nssv_COMPILER_VERSION( major, minor, patch )  (10 * ( 10 * major + minor) + patch)
  
  #if defined(__clang__)
  # define nssv_COMPILER_CLANG_VERSION  nssv_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
  #else
  # define nssv_COMPILER_CLANG_VERSION    0
  #endif
  
  #if defined(__GNUC__) && !defined(__clang__)
  # define nssv_COMPILER_GNUC_VERSION  nssv_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
  #else
  # define nssv_COMPILER_GNUC_VERSION    0
  #endif
  
  // half-open range [lo..hi):
  #define nssv_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
  
  // Presence of language and library features:
  
  #ifdef _HAS_CPP0X
  # define nssv_HAS_CPP0X  _HAS_CPP0X
  #else
  # define nssv_HAS_CPP0X  0
  #endif
  
  // Unless defined otherwise below, consider VC14 as C++11 for variant-lite:
  
  #if nssv_COMPILER_MSVC_VER >= 1900
  # undef  nssv_CPP11_OR_GREATER
  # define nssv_CPP11_OR_GREATER  1
  #endif
  
  #define nssv_CPP11_90   (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1500)
  #define nssv_CPP11_100  (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1600)
  #define nssv_CPP11_110  (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1700)
  #define nssv_CPP11_120  (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1800)
  #define nssv_CPP11_140  (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1900)
  #define nssv_CPP11_141  (nssv_CPP11_OR_GREATER_ || nssv_COMPILER_MSVC_VER >= 1910)
  
  #define nssv_CPP14_000  (nssv_CPP14_OR_GREATER)
  #define nssv_CPP17_000  (nssv_CPP17_OR_GREATER)
  
  // Presence of C++11 language features:
  
  #define nssv_HAVE_CONSTEXPR_11          nssv_CPP11_140
  #define nssv_HAVE_EXPLICIT_CONVERSION   nssv_CPP11_140
  #define nssv_HAVE_INLINE_NAMESPACE      nssv_CPP11_140
  #define nssv_HAVE_NOEXCEPT              nssv_CPP11_140
  #define nssv_HAVE_NULLPTR               nssv_CPP11_100
  #define nssv_HAVE_REF_QUALIFIER         nssv_CPP11_140
  #define nssv_HAVE_UNICODE_LITERALS      nssv_CPP11_140
  #define nssv_HAVE_USER_DEFINED_LITERALS nssv_CPP11_140
  #define nssv_HAVE_WCHAR16_T             nssv_CPP11_100
  #define nssv_HAVE_WCHAR32_T             nssv_CPP11_100
  
  #if ! ( ( nssv_CPP11 && nssv_COMPILER_CLANG_VERSION ) || nssv_BETWEEN( nssv_COMPILER_CLANG_VERSION, 300, 400 ) )
  # define nssv_HAVE_STD_DEFINED_LITERALS  nssv_CPP11_140
  #endif
  
  // Presence of C++14 language features:
  
  #define nssv_HAVE_CONSTEXPR_14          nssv_CPP14_000
  
  // Presence of C++17 language features:
  
  #define nssv_HAVE_NODISCARD             nssv_CPP17_000
  
  // Presence of C++ library features:
  
  #define nssv_HAVE_STD_HASH              nssv_CPP11_120
  
  // C++ feature usage:
  
  #if nssv_HAVE_CONSTEXPR_11
  # define nssv_constexpr  constexpr
  #else
  # define nssv_constexpr  /*constexpr*/
  #endif
  
  #if  nssv_HAVE_CONSTEXPR_14
  # define nssv_constexpr14  constexpr
  #else
  # define nssv_constexpr14  /*constexpr*/
  #endif
  
  #if nssv_HAVE_EXPLICIT_CONVERSION
  # define nssv_explicit  explicit
  #else
  # define nssv_explicit  /*explicit*/
  #endif
  
  #if nssv_HAVE_INLINE_NAMESPACE
  # define nssv_inline_ns  inline
  #else
  # define nssv_inline_ns  /*inline*/
  #endif
  
  #if nssv_HAVE_NOEXCEPT
  # define nssv_noexcept  noexcept
  #else
  # define nssv_noexcept  /*noexcept*/
  #endif
  
  //#if nssv_HAVE_REF_QUALIFIER
  //# define nssv_ref_qual  &
  //# define nssv_refref_qual  &&
  //#else
  //# define nssv_ref_qual  /*&*/
  //# define nssv_refref_qual  /*&&*/
  //#endif
  
  #if nssv_HAVE_NULLPTR
  # define nssv_nullptr  nullptr
  #else
  # define nssv_nullptr  NULL
  #endif
  
  #if nssv_HAVE_NODISCARD
  # define nssv_nodiscard  [[nodiscard]]
  #else
  # define nssv_nodiscard  /*[[nodiscard]]*/
  #endif
  
  // Additional includes:
  
  #include <algorithm>
  #include <cassert>
  #include <iterator>
  #include <limits>
  #include <ostream>
  #include <string>   // std::char_traits<>
  
  #if ! nssv_CONFIG_NO_EXCEPTIONS
  # include <stdexcept>
  #endif
  
  #if nssv_CPP11_OR_GREATER
  # include <type_traits>
  #endif
  
  // Clang, GNUC, MSVC warning suppression macros:
  
  #if defined(__clang__)
  # pragma clang diagnostic ignored "-Wreserved-user-defined-literal"
  # pragma clang diagnostic push
  # pragma clang diagnostic ignored "-Wuser-defined-literals"
  #elif defined(__GNUC__)
  # pragma  GCC  diagnostic push
  # pragma  GCC  diagnostic ignored "-Wliteral-suffix"
  #endif // __clang__
  
  #if nssv_COMPILER_MSVC_VERSION >= 140
  # define nssv_SUPPRESS_MSGSL_WARNING(expr)        [[gsl::suppress(expr)]]
  # define nssv_SUPPRESS_MSVC_WARNING(code, descr)  __pragma(warning(suppress: code) )
  # define nssv_DISABLE_MSVC_WARNINGS(codes)        __pragma(warning(push))  __pragma(warning(disable: codes))
  #else
  # define nssv_SUPPRESS_MSGSL_WARNING(expr)
  # define nssv_SUPPRESS_MSVC_WARNING(code, descr)
  # define nssv_DISABLE_MSVC_WARNINGS(codes)
  #endif
  
  #if defined(__clang__)
  # define nssv_RESTORE_WARNINGS()  _Pragma("clang diagnostic pop")
  #elif defined(__GNUC__)
  # define nssv_RESTORE_WARNINGS()  _Pragma("GCC diagnostic pop")
  #elif nssv_COMPILER_MSVC_VERSION >= 140
  # define nssv_RESTORE_WARNINGS()  __pragma(warning(pop ))
  #else
  # define nssv_RESTORE_WARNINGS()
  #endif
  
  // Suppress the following MSVC (GSL) warnings:
  // - C4455, non-gsl   : 'operator ""sv': literal suffix identifiers that do not
  //                      start with an underscore are reserved
  // - C26472, gsl::t.1 : don't use a static_cast for arithmetic conversions;
  //                      use brace initialization, gsl::narrow_cast or gsl::narow
  // - C26481: gsl::b.1 : don't use pointer arithmetic. Use span instead
  
  nssv_DISABLE_MSVC_WARNINGS( 4455 26481 26472 )
  //nssv_DISABLE_CLANG_WARNINGS( "-Wuser-defined-literals" )
  //nssv_DISABLE_GNUC_WARNINGS( -Wliteral-suffix )
  
  namespace nonstd { namespace sv_lite {
  
  template
  <
      class CharT,
      class Traits = std::char_traits<CharT>
  >
  class basic_string_view;
  
  //
  // basic_string_view:
  //
  
  template
  <
      class CharT,
      class Traits /* = std::char_traits<CharT> */
  >
  class basic_string_view
  {
  public:
      // Member types:
  
      typedef Traits traits_type;
      typedef CharT  value_type;
  
      typedef CharT       * pointer;
      typedef CharT const * const_pointer;
      typedef CharT       & reference;
      typedef CharT const & const_reference;
  
      typedef const_pointer iterator;
      typedef const_pointer const_iterator;
      typedef std::reverse_iterator< const_iterator > reverse_iterator;
      typedef	std::reverse_iterator< const_iterator > const_reverse_iterator;
  
      typedef std::size_t     size_type;
      typedef std::ptrdiff_t  difference_type;
  
      // 24.4.2.1 Construction and assignment:
  
      nssv_constexpr basic_string_view() nssv_noexcept
          : data_( nssv_nullptr )
          , size_( 0 )
      {}
  
  #if nssv_CPP11_OR_GREATER
      nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept = default;
  #else
      nssv_constexpr basic_string_view( basic_string_view const & other ) nssv_noexcept
          : data_( other.data_)
          , size_( other.size_)
      {}
  #endif
  
      nssv_constexpr basic_string_view( CharT const * s, size_type count )
          : data_( s )
          , size_( count )
      {}
  
      nssv_constexpr basic_string_view( CharT const * s)
          : data_( s )
          , size_( Traits::length(s) )
      {}
  
      // Assignment:
  
  #if nssv_CPP11_OR_GREATER
      nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept = default;
  #else
      nssv_constexpr14 basic_string_view & operator=( basic_string_view const & other ) nssv_noexcept
      {
          data_ = other.data_;
          size_ = other.size_;
          return *this;
      }
  #endif
  
      // 24.4.2.2 Iterator support:
  
      nssv_constexpr const_iterator begin()  const nssv_noexcept { return data_;         }
      nssv_constexpr const_iterator end()    const nssv_noexcept { return data_ + size_; }
  
      nssv_constexpr const_iterator cbegin() const nssv_noexcept { return begin(); }
      nssv_constexpr const_iterator cend()   const nssv_noexcept { return end();   }
  
      nssv_constexpr const_reverse_iterator rbegin()  const nssv_noexcept { return const_reverse_iterator( end() );   }
      nssv_constexpr const_reverse_iterator rend()    const nssv_noexcept { return const_reverse_iterator( begin() ); }
  
      nssv_constexpr const_reverse_iterator crbegin() const nssv_noexcept { return rbegin(); }
      nssv_constexpr const_reverse_iterator crend()   const nssv_noexcept { return rend();   }
  
      // 24.4.2.3 Capacity:
  
      nssv_constexpr size_type size()     const nssv_noexcept { return size_; }
      nssv_constexpr size_type length()   const nssv_noexcept { return size_; }
      nssv_constexpr size_type max_size() const nssv_noexcept { return (std::numeric_limits< size_type >::max)(); }
  
      // since C++20
      nssv_nodiscard nssv_constexpr bool empty() const nssv_noexcept
      {
          return 0 == size_;
      }
  
      // 24.4.2.4 Element access:
  
      nssv_constexpr const_reference operator[]( size_type pos ) const
      {
          return data_at( pos );
      }
  
      nssv_constexpr14 const_reference at( size_type pos ) const
      {
  #if nssv_CONFIG_NO_EXCEPTIONS
          assert( pos < size() );
  #else
          if ( pos >= size() )
          {
              throw std::out_of_range("nonst::string_view::at()");
          }
  #endif
          return data_at( pos );
      }
  
      nssv_constexpr const_reference front() const { return data_at( 0 );          }
      nssv_constexpr const_reference back()  const { return data_at( size() - 1 ); }
  
      nssv_constexpr const_pointer   data()  const nssv_noexcept { return data_; }
  
      // 24.4.2.5 Modifiers:
  
      nssv_constexpr14 void remove_prefix( size_type n )
      {
          assert( n <= size() );
          data_ += n;
          size_ -= n;
      }
  
      nssv_constexpr14 void remove_suffix( size_type n )
      {
          assert( n <= size() );
          size_ -= n;
      }
  
      nssv_constexpr14 void swap( basic_string_view & other ) nssv_noexcept
      {
          using std::swap;
          swap( data_, other.data_ );
          swap( size_, other.size_ );
      }
  
      // 24.4.2.6 String operations:
  
      size_type copy( CharT * dest, size_type n, size_type pos = 0 ) const
      {
  #if nssv_CONFIG_NO_EXCEPTIONS
          assert( pos <= size() );
  #else
          if ( pos > size() )
          {
              throw std::out_of_range("nonst::string_view::copy()");
          }
  #endif
          const size_type rlen = (std::min)( n, size() - pos );
  
          (void) Traits::copy( dest, data() + pos, rlen );
  
          return rlen;
      }
  
      nssv_constexpr14 basic_string_view substr( size_type pos = 0, size_type n = npos ) const
      {
  #if nssv_CONFIG_NO_EXCEPTIONS
          assert( pos <= size() );
  #else
          if ( pos > size() )
          {
              throw std::out_of_range("nonst::string_view::substr()");
          }
  #endif
          return basic_string_view( data() + pos, (std::min)( n, size() - pos ) );
      }
  
      // compare(), 6x:
  
      nssv_constexpr14 int compare( basic_string_view other ) const nssv_noexcept // (1)
      {
          if ( const int result = Traits::compare( data(), other.data(), (std::min)( size(), other.size() ) ) )
              return result;
  
          return size() == other.size() ? 0 : size() < other.size() ? -1 : 1;
      }
  
      nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other ) const // (2)
      {
          return substr( pos1, n1 ).compare( other );
      }
  
      nssv_constexpr int compare( size_type pos1, size_type n1, basic_string_view other, size_type pos2, size_type n2 ) const // (3)
      {
          return substr( pos1, n1 ).compare( other.substr( pos2, n2 ) );
      }
  
      nssv_constexpr int compare( CharT const * s ) const // (4)
      {
          return compare( basic_string_view( s ) );
      }
  
      nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s ) const // (5)
      {
          return substr( pos1, n1 ).compare( basic_string_view( s ) );
      }
  
      nssv_constexpr int compare( size_type pos1, size_type n1, CharT const * s, size_type n2 ) const // (6)
      {
          return substr( pos1, n1 ).compare( basic_string_view( s, n2 ) );
      }
  
      // 24.4.2.7 Searching:
  
      // starts_with(), 3x, since C++20:
  
      nssv_constexpr bool starts_with( basic_string_view v ) const nssv_noexcept  // (1)
      {
          return size() >= v.size() && compare( 0, v.size(), v ) == 0;
      }
  
      nssv_constexpr bool starts_with( CharT c ) const nssv_noexcept  // (2)
      {
          return starts_with( basic_string_view( &c, 1 ) );
      }
  
      nssv_constexpr bool starts_with( CharT const * s ) const  // (3)
      {
          return starts_with( basic_string_view( s ) );
      }
  
      // ends_with(), 3x, since C++20:
  
      nssv_constexpr bool ends_with( basic_string_view v ) const nssv_noexcept  // (1)
      {
          return size() >= v.size() && compare( size() - v.size(), npos, v ) == 0;
      }
  
      nssv_constexpr bool ends_with( CharT c ) const nssv_noexcept  // (2)
      {
          return ends_with( basic_string_view( &c, 1 ) );
      }
  
      nssv_constexpr bool ends_with( CharT const * s ) const  // (3)
      {
          return ends_with( basic_string_view( s ) );
      }
  
      // find(), 4x:
  
      nssv_constexpr14 size_type find( basic_string_view v, size_type pos = 0 ) const nssv_noexcept  // (1)
      {
          return assert( v.size() == 0 || v.data() != nssv_nullptr )
              , pos >= size()
              ? npos
              : to_pos( std::search( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) );
      }
  
      nssv_constexpr14 size_type find( CharT c, size_type pos = 0 ) const nssv_noexcept  // (2)
      {
          return find( basic_string_view( &c, 1 ), pos );
      }
  
      nssv_constexpr14 size_type find( CharT const * s, size_type pos, size_type n ) const  // (3)
      {
          return find( basic_string_view( s, n ), pos );
      }
  
      nssv_constexpr14 size_type find( CharT const * s, size_type pos = 0 ) const  // (4)
      {
          return find( basic_string_view( s ), pos );
      }
  
      // rfind(), 4x:
  
      nssv_constexpr14 size_type rfind( basic_string_view v, size_type pos = npos ) const nssv_noexcept  // (1)
      {
          if ( size() < v.size() )
              return npos;
  
          if ( v.empty() )
              return (std::min)( size(), pos );
  
          const_iterator last   = cbegin() + (std::min)( size() - v.size(), pos ) + v.size();
          const_iterator result = std::find_end( cbegin(), last, v.cbegin(), v.cend(), Traits::eq );
  
          return result != last ? size_type( result - cbegin() ) : npos;
      }
  
      nssv_constexpr14 size_type rfind( CharT c, size_type pos = npos ) const nssv_noexcept  // (2)
      {
          return rfind( basic_string_view( &c, 1 ), pos );
      }
  
      nssv_constexpr14 size_type rfind( CharT const * s, size_type pos, size_type n ) const  // (3)
      {
          return rfind( basic_string_view( s, n ), pos );
      }
  
      nssv_constexpr14 size_type rfind( CharT const * s, size_type pos = npos ) const  // (4)
      {
          return rfind( basic_string_view( s ), pos );
      }
  
      // find_first_of(), 4x:
  
      nssv_constexpr size_type find_first_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept  // (1)
      {
          return pos >= size()
              ? npos
              : to_pos( std::find_first_of( cbegin() + pos, cend(), v.cbegin(), v.cend(), Traits::eq ) );
      }
  
      nssv_constexpr size_type find_first_of( CharT c, size_type pos = 0 ) const nssv_noexcept  // (2)
      {
          return find_first_of( basic_string_view( &c, 1 ), pos );
      }
  
      nssv_constexpr size_type find_first_of( CharT const * s, size_type pos, size_type n ) const  // (3)
      {
          return find_first_of( basic_string_view( s, n ), pos );
      }
  
      nssv_constexpr size_type find_first_of(  CharT const * s, size_type pos = 0 ) const  // (4)
      {
          return find_first_of( basic_string_view( s ), pos );
      }
  
      // find_last_of(), 4x:
  
      nssv_constexpr size_type find_last_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept  // (1)
      {
          return empty()
              ? npos
              : pos >= size()
              ? find_last_of( v, size() - 1 )
              : to_pos( std::find_first_of( const_reverse_iterator( cbegin() + pos + 1 ), crend(), v.cbegin(), v.cend(), Traits::eq ) );
      }
  
      nssv_constexpr size_type find_last_of( CharT c, size_type pos = npos ) const nssv_noexcept  // (2)
      {
          return find_last_of( basic_string_view( &c, 1 ), pos );
      }
  
      nssv_constexpr size_type find_last_of( CharT const * s, size_type pos, size_type count ) const  // (3)
      {
          return find_last_of( basic_string_view( s, count ), pos );
      }
  
      nssv_constexpr size_type find_last_of( CharT const * s, size_type pos = npos ) const  // (4)
      {
          return find_last_of( basic_string_view( s ), pos );
      }
  
      // find_first_not_of(), 4x:
  
      nssv_constexpr size_type find_first_not_of( basic_string_view v, size_type pos = 0 ) const nssv_noexcept  // (1)
      {
          return pos >= size()
              ? npos
              : to_pos( std::find_if( cbegin() + pos, cend(), not_in_view( v ) ) );
      }
  
      nssv_constexpr size_type find_first_not_of( CharT c, size_type pos = 0 ) const nssv_noexcept  // (2)
      {
          return find_first_not_of( basic_string_view( &c, 1 ), pos );
      }
  
      nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos, size_type count ) const  // (3)
      {
          return find_first_not_of( basic_string_view( s, count ), pos );
      }
  
      nssv_constexpr size_type find_first_not_of( CharT const * s, size_type pos = 0 ) const  // (4)
      {
          return find_first_not_of( basic_string_view( s ), pos );
      }
  
      // find_last_not_of(), 4x:
  
      nssv_constexpr size_type find_last_not_of( basic_string_view v, size_type pos = npos ) const nssv_noexcept  // (1)
      {
          return empty()
              ? npos
              : pos >= size()
              ? find_last_not_of( v, size() - 1 )
              : to_pos( std::find_if( const_reverse_iterator( cbegin() + pos + 1 ), crend(), not_in_view( v ) ) );
      }
  
      nssv_constexpr size_type find_last_not_of( CharT c, size_type pos = npos ) const nssv_noexcept  // (2)
      {
          return find_last_not_of( basic_string_view( &c, 1 ), pos );
      }
  
      nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos, size_type count ) const  // (3)
      {
          return find_last_not_of( basic_string_view( s, count ), pos );
      }
  
      nssv_constexpr size_type find_last_not_of( CharT const * s, size_type pos = npos ) const  // (4)
      {
          return find_last_not_of( basic_string_view( s ), pos );
      }
  
      // Constants:
  
  #if nssv_CPP17_OR_GREATER
      static nssv_constexpr size_type npos = size_type(-1);
  #elif nssv_CPP11_OR_GREATER
      enum : size_type { npos = size_type(-1) };
  #else
      enum { npos = size_type(-1) };
  #endif
  
  private:
      struct not_in_view
      {
          const basic_string_view v;
  
          nssv_constexpr not_in_view( basic_string_view v ) : v( v ) {}
  
          nssv_constexpr bool operator()( CharT c ) const
          {
              return npos == v.find_first_of( c );
          }
      };
  
      nssv_constexpr size_type to_pos( const_iterator it ) const
      {
          return it == cend() ? npos : size_type( it - cbegin() );
      }
  
      nssv_constexpr size_type to_pos( const_reverse_iterator it ) const
      {
          return it == crend() ? npos : size_type( crend() - it - 1 );
      }
  
      nssv_constexpr const_reference data_at( size_type pos ) const
      {
  #if nssv_BETWEEN( nssv_COMPILER_GNUC_VERSION, 1, 500 )
          return data_[pos];
  #else
          return assert( pos < size() ), data_[pos];
  #endif
      }
  
  private:
      const_pointer data_;
      size_type     size_;
  
  public:
  #if nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
  
      template< class Allocator >
      basic_string_view( std::basic_string<CharT, Traits, Allocator> const & s ) nssv_noexcept
          : data_( s.data() )
          , size_( s.size() )
      {}
  
  #if nssv_HAVE_EXPLICIT_CONVERSION
  
      template< class Allocator >
      explicit operator std::basic_string<CharT, Traits, Allocator>() const
      {
          return to_string( Allocator() );
      }
  
  #endif // nssv_HAVE_EXPLICIT_CONVERSION
  
  #if nssv_CPP11_OR_GREATER
  
      template< class Allocator = std::allocator<CharT> >
      std::basic_string<CharT, Traits, Allocator>
      to_string( Allocator const & a = Allocator() ) const
      {
          return std::basic_string<CharT, Traits, Allocator>( begin(), end(), a );
      }
  
  #else
  
      std::basic_string<CharT, Traits>
      to_string() const
      {
          return std::basic_string<CharT, Traits>( begin(), end() );
      }
  
      template< class Allocator >
      std::basic_string<CharT, Traits, Allocator>
      to_string( Allocator const & a ) const
      {
          return std::basic_string<CharT, Traits, Allocator>( begin(), end(), a );
      }
  
  #endif // nssv_CPP11_OR_GREATER
  
  #endif // nssv_CONFIG_CONVERSION_STD_STRING_CLASS_METHODS
  };
  
  //
  // Non-member functions:
  //
  
  // 24.4.3 Non-member comparison functions:
  // lexicographically compare two string views (function template):
  
  template< class CharT, class Traits >
  nssv_constexpr bool operator== (
      basic_string_view <CharT, Traits> lhs,
      basic_string_view <CharT, Traits> rhs ) nssv_noexcept
  { return lhs.compare( rhs ) == 0 ; }
  
  template< class CharT, class Traits >
  nssv_constexpr bool operator!= (
      basic_string_view <CharT, Traits> lhs,
      basic_string_view <CharT, Traits> rhs ) nssv_noexcept
  { return lhs.compare( rhs ) != 0 ; }
  
  template< class CharT, class Traits >
  nssv_constexpr bool operator< (
      basic_string_view <CharT, Traits> lhs,
      basic_string_view <CharT, Traits> rhs ) nssv_noexcept
  { return lhs.compare( rhs ) < 0 ; }
  
  template< class CharT, class Traits >
  nssv_constexpr bool operator<= (
      basic_string_view <CharT, Traits> lhs,
      basic_string_view <CharT, Traits> rhs ) nssv_noexcept
  { return lhs.compare( rhs ) <= 0 ; }
  
  template< class CharT, class Traits >
  nssv_constexpr bool operator> (
      basic_string_view <CharT, Traits> lhs,
      basic_string_view <CharT, Traits> rhs ) nssv_noexcept
  { return lhs.compare( rhs ) > 0 ; }
  
  template< class CharT, class Traits >
  nssv_constexpr bool operator>= (
      basic_string_view <CharT, Traits> lhs,
      basic_string_view <CharT, Traits> rhs ) nssv_noexcept
  { return lhs.compare( rhs ) >= 0 ; }
  
  // Let S be basic_string_view<CharT, Traits>, and sv be an instance of S.
  // Implementations shall provide sufficient additional overloads marked
  // constexpr and noexcept so that an object t with an implicit conversion
  // to S can be compared according to Table 67.
  
  #if nssv_CPP11_OR_GREATER && ! nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 100, 141 )
  
  #define nssv_BASIC_STRING_VIEW_I(T,U)  typename std::decay< basic_string_view<T,U> >::type
  
  #if nssv_BETWEEN( nssv_COMPILER_MSVC_VERSION, 140, 150 )
  # define nssv_MSVC_ORDER(x)  , int=x
  #else
  # define nssv_MSVC_ORDER(x)  /*, int=x*/
  #endif
  
  // ==
  
  template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
  nssv_constexpr bool operator==(
           basic_string_view  <CharT, Traits> lhs,
      nssv_BASIC_STRING_VIEW_I(CharT, Traits) rhs ) nssv_noexcept
  { return lhs.compare( rhs ) == 0; }
  
  template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
  nssv_constexpr bool operator==(
      nssv_BASIC_STRING_VIEW_I(CharT, Traits) lhs,
           basic_string_view  <CharT, Traits> rhs ) nssv_noexcept
  { return lhs.size() == rhs.size() && lhs.compare( rhs ) == 0; }
  
  // !=
  
  template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
  nssv_constexpr bool operator!= (
           basic_string_view  < CharT, Traits > lhs,
      nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
  { return lhs.size() != rhs.size() || lhs.compare( rhs ) != 0 ; }
  
  template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
  nssv_constexpr bool operator!= (
      nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
           basic_string_view  < CharT, Traits > rhs ) nssv_noexcept
  { return lhs.compare( rhs ) != 0 ; }
  
  // <
  
  template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
  nssv_constexpr bool operator< (
           basic_string_view  < CharT, Traits > lhs,
      nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
  { return lhs.compare( rhs ) < 0 ; }
  
  template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
  nssv_constexpr bool operator< (
      nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
           basic_string_view  < CharT, Traits > rhs ) nssv_noexcept
  { return lhs.compare( rhs ) < 0 ; }
  
  // <=
  
  template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
  nssv_constexpr bool operator<= (
           basic_string_view  < CharT, Traits > lhs,
      nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
  { return lhs.compare( rhs ) <= 0 ; }
  
  template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
  nssv_constexpr bool operator<= (
      nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
           basic_string_view  < CharT, Traits > rhs ) nssv_noexcept
  { return lhs.compare( rhs ) <= 0 ; }
  
  // >
  
  template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
  nssv_constexpr bool operator> (
           basic_string_view  < CharT, Traits > lhs,
      nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
  { return lhs.compare( rhs ) > 0 ; }
  
  template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
  nssv_constexpr bool operator> (
      nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
           basic_string_view  < CharT, Traits > rhs ) nssv_noexcept
  { return lhs.compare( rhs ) > 0 ; }
  
  // >=
  
  template< class CharT, class Traits  nssv_MSVC_ORDER(1) >
  nssv_constexpr bool operator>= (
           basic_string_view  < CharT, Traits > lhs,
      nssv_BASIC_STRING_VIEW_I( CharT, Traits ) rhs ) nssv_noexcept
  { return lhs.compare( rhs ) >= 0 ; }
  
  template< class CharT, class Traits  nssv_MSVC_ORDER(2) >
  nssv_constexpr bool operator>= (
      nssv_BASIC_STRING_VIEW_I( CharT, Traits ) lhs,
           basic_string_view  < CharT, Traits > rhs ) nssv_noexcept
  { return lhs.compare( rhs ) >= 0 ; }
  
  #undef nssv_MSVC_ORDER
  #undef nssv_BASIC_STRING_VIEW_I
  
  #endif // nssv_CPP11_OR_GREATER
  
  // 24.4.4 Inserters and extractors:
  
  namespace detail {
  
  template< class Stream >
  void write_padding( Stream & os, std::streamsize n )
  {
      for ( std::streamsize i = 0; i < n; ++i )
          os.rdbuf()->sputc( os.fill() );
  }
  
  template< class Stream, class View >
  Stream & write_to_stream( Stream & os, View const & sv )
  {
      typename Stream::sentry sentry( os );
  
      if ( !os )
          return os;
  
      const std::streamsize length = static_cast<std::streamsize>( sv.length() );
  
      // Whether, and how, to pad:
      const bool      pad = ( length < os.width() );
      const bool left_pad = pad && ( os.flags() & std::ios_base::adjustfield ) == std::ios_base::right;
  
      if ( left_pad )
          write_padding( os, os.width() - length );
  
      // Write span characters:
      os.rdbuf()->sputn( sv.begin(), length );
  
      if ( pad && !left_pad )
          write_padding( os, os.width() - length );
  
      // Reset output stream width:
      os.width( 0 );
  
      return os;
  }
  
  } // namespace detail
  
  template< class CharT, class Traits >
  std::basic_ostream<CharT, Traits> &
  operator<<(
      std::basic_ostream<CharT, Traits>& os,
      basic_string_view <CharT, Traits> sv )
  {
      return detail::write_to_stream( os, sv );
  }
  
  // Several typedefs for common character types are provided:
  
  typedef basic_string_view<char>      string_view;
  typedef basic_string_view<wchar_t>   wstring_view;
  #if nssv_HAVE_WCHAR16_T
  typedef basic_string_view<char16_t>  u16string_view;
  typedef basic_string_view<char32_t>  u32string_view;
  #endif
  
  }} // namespace nonstd::sv_lite
  
  //
  // 24.4.6 Suffix for basic_string_view literals:
  //
  
  #if nssv_HAVE_USER_DEFINED_LITERALS
  
  namespace nonstd {
  nssv_inline_ns namespace literals {
  nssv_inline_ns namespace string_view_literals {
  
  #if nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
  
  nssv_constexpr nonstd::sv_lite::string_view operator "" sv( const char* str, size_t len ) nssv_noexcept  // (1)
  {
      return nonstd::sv_lite::string_view{ str, len };
  }
  
  nssv_constexpr nonstd::sv_lite::u16string_view operator "" sv( const char16_t* str, size_t len ) nssv_noexcept  // (2)
  {
      return nonstd::sv_lite::u16string_view{ str, len };
  }
  
  nssv_constexpr nonstd::sv_lite::u32string_view operator "" sv( const char32_t* str, size_t len ) nssv_noexcept  // (3)
  {
      return nonstd::sv_lite::u32string_view{ str, len };
  }
  
  nssv_constexpr nonstd::sv_lite::wstring_view operator "" sv( const wchar_t* str, size_t len ) nssv_noexcept  // (4)
  {
      return nonstd::sv_lite::wstring_view{ str, len };
  }
  
  #endif // nssv_CONFIG_STD_SV_OPERATOR && nssv_HAVE_STD_DEFINED_LITERALS
  
  #if nssv_CONFIG_USR_SV_OPERATOR
  
  nssv_constexpr nonstd::sv_lite::string_view operator "" _sv( const char* str, size_t len ) nssv_noexcept  // (1)
  {
      return nonstd::sv_lite::string_view{ str, len };
  }
  
  nssv_constexpr nonstd::sv_lite::u16string_view operator "" _sv( const char16_t* str, size_t len ) nssv_noexcept  // (2)
  {
      return nonstd::sv_lite::u16string_view{ str, len };
  }
  
  nssv_constexpr nonstd::sv_lite::u32string_view operator "" _sv( const char32_t* str, size_t len ) nssv_noexcept  // (3)
  {
      return nonstd::sv_lite::u32string_view{ str, len };
  }
  
  nssv_constexpr nonstd::sv_lite::wstring_view operator "" _sv( const wchar_t* str, size_t len ) nssv_noexcept  // (4)
  {
      return nonstd::sv_lite::wstring_view{ str, len };
  }
  
  #endif // nssv_CONFIG_USR_SV_OPERATOR
  
  }}} // namespace nonstd::literals::string_view_literals
  
  #endif
  
  //
  // Extensions for std::string:
  //
  
  #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
  
  namespace nonstd {
  namespace sv_lite {
  
  // Exclude MSVC 14 (19.00): it yields ambiguous to_string():
  
  #if nssv_CPP11_OR_GREATER && nssv_COMPILER_MSVC_VERSION != 140
  
  template< class CharT, class Traits, class Allocator = std::allocator<CharT> >
  std::basic_string<CharT, Traits, Allocator>
  to_string( basic_string_view<CharT, Traits> v, Allocator const & a = Allocator() )
  {
      return std::basic_string<CharT,Traits, Allocator>( v.begin(), v.end(), a );
  }
  
  #else
  
  template< class CharT, class Traits >
  std::basic_string<CharT, Traits>
  to_string( basic_string_view<CharT, Traits> v )
  {
      return std::basic_string<CharT, Traits>( v.begin(), v.end() );
  }
  
  template< class CharT, class Traits, class Allocator >
  std::basic_string<CharT, Traits, Allocator>
  to_string( basic_string_view<CharT, Traits> v, Allocator const & a )
  {
      return std::basic_string<CharT, Traits, Allocator>( v.begin(), v.end(), a );
  }
  
  #endif // nssv_CPP11_OR_GREATER
  
  template< class CharT, class Traits, class Allocator >
  basic_string_view<CharT, Traits>
  to_string_view( std::basic_string<CharT, Traits, Allocator> const & s )
  {
      return basic_string_view<CharT, Traits>( s.data(), s.size() );
  }
  
  }} // namespace nonstd::sv_lite
  
  #endif // nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
  
  //
  // make types and algorithms available in namespace nonstd:
  //
  
  namespace nonstd {
  
  using sv_lite::basic_string_view;
  using sv_lite::string_view;
  using sv_lite::wstring_view;
  
  #if nssv_HAVE_WCHAR16_T
  using sv_lite::u16string_view;
  #endif
  #if nssv_HAVE_WCHAR32_T
  using sv_lite::u32string_view;
  #endif
  
  // literal "sv"
  
  using sv_lite::operator==;
  using sv_lite::operator!=;
  using sv_lite::operator<;
  using sv_lite::operator<=;
  using sv_lite::operator>;
  using sv_lite::operator>=;
  
  using sv_lite::operator<<;
  
  #if nssv_CONFIG_CONVERSION_STD_STRING_FREE_FUNCTIONS
  using sv_lite::to_string;
  using sv_lite::to_string_view;
  #endif
  
  } // namespace nonstd
  
  // 24.4.5 Hash support (C++11):
  
  // Note: The hash value of a string view object is equal to the hash value of
  // the corresponding string object.
  
  #if nssv_HAVE_STD_HASH
  
  #include <functional>
  
  namespace std {
  
  template<>
  struct hash< nonstd::string_view >
  {
  public:
      std::size_t operator()( nonstd::string_view v ) const nssv_noexcept
      {
          return std::hash<std::string>()( std::string( v.data(), v.size() ) );
      }
  };
  
  template<>
  struct hash< nonstd::wstring_view >
  {
  public:
      std::size_t operator()( nonstd::wstring_view v ) const nssv_noexcept
      {
          return std::hash<std::wstring>()( std::wstring( v.data(), v.size() ) );
      }
  };
  
  template<>
  struct hash< nonstd::u16string_view >
  {
  public:
      std::size_t operator()( nonstd::u16string_view v ) const nssv_noexcept
      {
          return std::hash<std::u16string>()( std::u16string( v.data(), v.size() ) );
      }
  };
  
  template<>
  struct hash< nonstd::u32string_view >
  {
  public:
      std::size_t operator()( nonstd::u32string_view v ) const nssv_noexcept
      {
          return std::hash<std::u32string>()( std::u32string( v.data(), v.size() ) );
      }
  };
  
  } // namespace std
  
  #endif // nssv_HAVE_STD_HASH
  
  nssv_RESTORE_WARNINGS()
  
  #endif // nssv_HAVE_STD_STRING_VIEW
  #endif // NONSTD_SV_LITE_H_INCLUDED
  
  
    // If there is another version of Hedley, then the newer one 
    // takes precedence.
    // See: https://github.com/nemequ/hedley
  /* Hedley - https://nemequ.github.io/hedley
   * Created by Evan Nemerson <evan@nemerson.com>
   *
   * To the extent possible under law, the author(s) have dedicated all
   * copyright and related and neighboring rights to this software to
   * the public domain worldwide. This software is distributed without
   * any warranty.
   *
   * For details, see <http://creativecommons.org/publicdomain/zero/1.0/>.
   * SPDX-License-Identifier: CC0-1.0
   */
  
  #if !defined(HEDLEY_VERSION) || (HEDLEY_VERSION < 9)
  #if defined(HEDLEY_VERSION)
  #  undef HEDLEY_VERSION
  #endif
  #define HEDLEY_VERSION 9
  
  #if defined(HEDLEY_STRINGIFY_EX)
  #  undef HEDLEY_STRINGIFY_EX
  #endif
  #define HEDLEY_STRINGIFY_EX(x) #x
  
  #if defined(HEDLEY_STRINGIFY)
  #  undef HEDLEY_STRINGIFY
  #endif
  #define HEDLEY_STRINGIFY(x) HEDLEY_STRINGIFY_EX(x)
  
  #if defined(HEDLEY_CONCAT_EX)
  #  undef HEDLEY_CONCAT_EX
  #endif
  #define HEDLEY_CONCAT_EX(a,b) a##b
  
  #if defined(HEDLEY_CONCAT)
  #  undef HEDLEY_CONCAT
  #endif
  #define HEDLEY_CONCAT(a,b) HEDLEY_CONCAT_EX(a,b)
  
  #if defined(HEDLEY_VERSION_ENCODE)
  #  undef HEDLEY_VERSION_ENCODE
  #endif
  #define HEDLEY_VERSION_ENCODE(major,minor,revision) (((major) * 1000000) + ((minor) * 1000) + (revision))
  
  #if defined(HEDLEY_VERSION_DECODE_MAJOR)
  #  undef HEDLEY_VERSION_DECODE_MAJOR
  #endif
  #define HEDLEY_VERSION_DECODE_MAJOR(version) ((version) / 1000000)
  
  #if defined(HEDLEY_VERSION_DECODE_MINOR)
  #  undef HEDLEY_VERSION_DECODE_MINOR
  #endif
  #define HEDLEY_VERSION_DECODE_MINOR(version) (((version) % 1000000) / 1000)
  
  #if defined(HEDLEY_VERSION_DECODE_REVISION)
  #  undef HEDLEY_VERSION_DECODE_REVISION
  #endif
  #define HEDLEY_VERSION_DECODE_REVISION(version) ((version) % 1000)
  
  #if defined(HEDLEY_GNUC_VERSION)
  #  undef HEDLEY_GNUC_VERSION
  #endif
  #if defined(__GNUC__) && defined(__GNUC_PATCHLEVEL__)
  #  define HEDLEY_GNUC_VERSION HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
  #elif defined(__GNUC__)
  #  define HEDLEY_GNUC_VERSION HEDLEY_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, 0)
  #endif
  
  #if defined(HEDLEY_GNUC_VERSION_CHECK)
  #  undef HEDLEY_GNUC_VERSION_CHECK
  #endif
  #if defined(HEDLEY_GNUC_VERSION)
  #  define HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (HEDLEY_GNUC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch))
  #else
  #  define HEDLEY_GNUC_VERSION_CHECK(major,minor,patch) (0)
  #endif
  
  #if defined(HEDLEY_MSVC_VERSION)
  #  undef HEDLEY_MSVC_VERSION
  #endif
  #if defined(_MSC_FULL_VER) && (_MSC_FULL_VER >= 140000000)
  #  define HEDLEY_MSVC_VERSION HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 10000000, (_MSC_FULL_VER % 10000000) / 100000, (_MSC_FULL_VER % 100000) / 100)
  #elif defined(_MSC_FULL_VER)
  #  define HEDLEY_MSVC_VERSION HEDLEY_VERSION_ENCODE(_MSC_FULL_VER / 1000000, (_MSC_FULL_VER % 1000000) / 10000, (_MSC_FULL_VER % 10000) / 10)
  #elif defined(_MSC_VER)
  #  define HEDLEY_MSVC_VERSION HEDLEY_VERSION_ENCODE(_MSC_VER / 100, _MSC_VER % 100, 0)
  #endif
  
  #if defined(HEDLEY_MSVC_VERSION_CHECK)
  #  undef HEDLEY_MSVC_VERSION_CHECK
  #endif
  #if !defined(_MSC_VER)
  #  define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (0)
  #elif defined(_MSC_VER) && (_MSC_VER >= 1400)
  #  define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 10000000) + (minor * 100000) + (patch)))
  #elif defined(_MSC_VER) && (_MSC_VER >= 1200)
  #  define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_FULL_VER >= ((major * 1000000) + (minor * 10000) + (patch)))
  #else
  #  define HEDLEY_MSVC_VERSION_CHECK(major,minor,patch) (_MSC_VER >= ((major * 100) + (minor)))
  #endif
  
  #if defined(HEDLEY_INTEL_VERSION)
  #  undef HEDLEY_INTEL_VERSION
  #endif
  #if defined(__INTEL_COMPILER) && defined(__INTEL_COMPILER_UPDATE)
  #  define HEDLEY_INTEL_VERSION HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, __INTEL_COMPILER_UPDATE)
  #elif defined(__INTEL_COMPILER)
  #  define HEDLEY_INTEL_VERSION HEDLEY_VERSION_ENCODE(__INTEL_COMPILER / 100, __INTEL_COMPILER % 100, 0)
  #endif
  
  #if defined(HEDLEY_INTEL_VERSION_CHECK)
  #  undef HEDLEY_INTEL_VERSION_CHECK
  #endif
  #if defined(HEDLEY_INTEL_VERSION)
  #  define HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (HEDLEY_INTEL_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch))
  #else
  #  define HEDLEY_INTEL_VERSION_CHECK(major,minor,patch) (0)
  #endif
  
  #if defined(HEDLEY_PGI_VERSION)
  #  undef HEDLEY_PGI_VERSION
  #endif
  #if defined(__PGI) && defined(__PGIC__) && defined(__PGIC_MINOR__) && defined(__PGIC_PATCHLEVEL__)
  #  define HEDLEY_PGI_VERSION HEDLEY_VERSION_ENCODE(__PGIC__, __PGIC_MINOR__, __PGIC_PATCHLEVEL__)
  #endif
  
  #if defined(HEDLEY_PGI_VERSION_CHECK)
  #  undef HEDLEY_PGI_VERSION_CHECK
  #endif
  #if defined(HEDLEY_PGI_VERSION)
  #  define HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (HEDLEY_PGI_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch))
  #else
  #  define HEDLEY_PGI_VERSION_CHECK(major,minor,patch) (0)
  #endif
  
  #if defined(HEDLEY_SUNPRO_VERSION)
  #  undef HEDLEY_SUNPRO_VERSION
  #endif
  #if defined(__SUNPRO_C) && (__SUNPRO_C > 0x1000)
  #  define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((((__SUNPRO_C >> 16) & 0xf) * 10) + ((__SUNPRO_C >> 12) & 0xf), (((__SUNPRO_C >> 8) & 0xf) * 10) + ((__SUNPRO_C >> 4) & 0xf), (__SUNPRO_C & 0xf) * 10)
  #elif defined(__SUNPRO_C)
  #  define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((__SUNPRO_C >> 8) & 0xf, (__SUNPRO_C >> 4) & 0xf, (__SUNPRO_C) & 0xf)
  #elif defined(__SUNPRO_CC) && (__SUNPRO_CC > 0x1000)
  #  define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((((__SUNPRO_CC >> 16) & 0xf) * 10) + ((__SUNPRO_CC >> 12) & 0xf), (((__SUNPRO_CC >> 8) & 0xf) * 10) + ((__SUNPRO_CC >> 4) & 0xf), (__SUNPRO_CC & 0xf) * 10)
  #elif defined(__SUNPRO_CC)
  #  define HEDLEY_SUNPRO_VERSION HEDLEY_VERSION_ENCODE((__SUNPRO_CC >> 8) & 0xf, (__SUNPRO_CC >> 4) & 0xf, (__SUNPRO_CC) & 0xf)
  #endif
  
  #if defined(HEDLEY_SUNPRO_VERSION_CHECK)
  #  undef HEDLEY_SUNPRO_VERSION_CHECK
  #endif
  #if defined(HEDLEY_SUNPRO_VERSION)
  #  define HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (HEDLEY_SUNPRO_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch))
  #else
  #  define HEDLEY_SUNPRO_VERSION_CHECK(major,minor,patch) (0)
  #endif
  
  #if defined(HEDLEY_EMSCRIPTEN_VERSION)
  #  undef HEDLEY_EMSCRIPTEN_VERSION
  #endif
  #if defined(__EMSCRIPTEN__)
  #  define HEDLEY_EMSCRIPTEN_VERSION HEDLEY_VERSION_ENCODE(__EMSCRIPTEN_major__, __EMSCRIPTEN_minor__, __EMSCRIPTEN_tiny__)
  #endif
  
  #if defined(HEDLEY_EMSCRIPTEN_VERSION_CHECK)
  #  undef HEDLEY_EMSCRIPTEN_VERSION_CHECK
  #endif
  #if defined(HEDLEY_EMSCRIPTEN_VERSION)
  #  define HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (HEDLEY_EMSCRIPTEN_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch))
  #else
  #  define HEDLEY_EMSCRIPTEN_VERSION_CHECK(major,minor,patch) (0)
  #endif
  
  #if defined(HEDLEY_ARM_VERSION)
  #  undef HEDLEY_ARM_VERSION
  #endif
  #if defined(__CC_ARM) && defined(__ARMCOMPILER_VERSION)
  #  define HEDLEY_ARM_VERSION HEDLEY_VERSION_ENCODE(__ARMCOMPILER_VERSION / 1000000, (__ARMCOMPILER_VERSION % 1000000) / 10000, (__ARMCOMPILER_VERSION % 10000) / 100)
  #elif defined(__CC_ARM) && defined(__ARMCC_VERSION)
  #  define HEDLEY_ARM_VERSION HEDLEY_VERSION_ENCODE(__ARMCC_VERSION / 1000000, (__ARMCC_VERSION % 1000000) / 10000, (__ARMCC_VERSION % 10000) / 100)
  #endif
  
  #if defined(HEDLEY_ARM_VERSION_CHECK)
  #  undef HEDLEY_ARM_VERSION_CHECK
  #endif
  #if defined(HEDLEY_ARM_VERSION)
  #  define HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (HEDLEY_ARM_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch))
  #else
  #  define HEDLEY_ARM_VERSION_CHECK(major,minor,patch) (0)
  #endif
  
  #if defined(HEDLEY_IBM_VERSION)
  #  undef HEDLEY_IBM_VERSION
  #endif
  #if defined(__ibmxl__)
  #  define HEDLEY_IBM_VERSION HEDLEY_VERSION_ENCODE(__ibmxl_version__, __ibmxl_release__, __ibmxl_modification__)
  #elif defined(__xlC__) && defined(__xlC_ver__)
  #  define HEDLEY_IBM_VERSION HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, (__xlC_ver__ >> 8) & 0xff)
  #elif defined(__xlC__)
  #  define HEDLEY_IBM_VERSION HEDLEY_VERSION_ENCODE(__xlC__ >> 8, __xlC__ & 0xff, 0)
  #endif
  
  #if defined(HEDLEY_IBM_VERSION_CHECK)
  #  undef HEDLEY_IBM_VERSION_CHECK
  #endif
  #if defined(HEDLEY_IBM_VERSION)
  #  define HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (HEDLEY_IBM_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch))
  #else
  #  define HEDLEY_IBM_VERSION_CHECK(major,minor,patch) (0)
  #endif
  
  #if defined(HEDLEY_TI_VERSION)
  #  undef HEDLEY_TI_VERSION
  #endif
  #if defined(__TI_COMPILER_VERSION__)
  #  define HEDLEY_TI_VERSION HEDLEY_VERSION_ENCODE(__TI_COMPILER_VERSION__ / 1000000, (__TI_COMPILER_VERSION__ % 1000000) / 1000, (__TI_COMPILER_VERSION__ % 1000))
  #endif
  
  #if defined(HEDLEY_TI_VERSION_CHECK)
  #  undef HEDLEY_TI_VERSION_CHECK
  #endif
  #if defined(HEDLEY_TI_VERSION)
  #  define HEDLEY_TI_VERSION_CHECK(major,minor,patch) (HEDLEY_TI_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch))
  #else
  #  define HEDLEY_TI_VERSION_CHECK(major,minor,patch) (0)
  #endif
  
  #if defined(HEDLEY_CRAY_VERSION)
  #  undef HEDLEY_CRAY_VERSION
  #endif
  #if defined(_CRAYC)
  #  if defined(_RELEASE_PATCHLEVEL)
  #    define HEDLEY_CRAY_VERSION HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, _RELEASE_PATCHLEVEL)
  #  else
  #    define HEDLEY_CRAY_VERSION HEDLEY_VERSION_ENCODE(_RELEASE_MAJOR, _RELEASE_MINOR, 0)
  #  endif
  #endif
  
  #if defined(HEDLEY_CRAY_VERSION_CHECK)
  #  undef HEDLEY_CRAY_VERSION_CHECK
  #endif
  #if defined(HEDLEY_CRAY_VERSION)
  #  define HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (HEDLEY_CRAY_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch))
  #else
  #  define HEDLEY_CRAY_VERSION_CHECK(major,minor,patch) (0)
  #endif
  
  #if defined(HEDLEY_IAR_VERSION)
  #  undef HEDLEY_IAR_VERSION
  #endif
  #if defined(__IAR_SYSTEMS_ICC__)
  #  if __VER__ > 1000
  #    define HEDLEY_IAR_VERSION HEDLEY_VERSION_ENCODE((__VER__ / 1000000), ((__VER__ / 1000) % 1000), (__VER__ % 1000))
  #  else
  #    define HEDLEY_IAR_VERSION HEDLEY_VERSION_ENCODE(VER / 100, __VER__ % 100, 0)
  #  endif
  #endif
  
  #if defined(HEDLEY_IAR_VERSION_CHECK)
  #  undef HEDLEY_IAR_VERSION_CHECK
  #endif
  #if defined(HEDLEY_IAR_VERSION)
  #  define HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (HEDLEY_IAR_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch))
  #else
  #  define HEDLEY_IAR_VERSION_CHECK(major,minor,patch) (0)
  #endif
  
  #if defined(HEDLEY_TINYC_VERSION)
  #  undef HEDLEY_TINYC_VERSION
  #endif
  #if defined(__TINYC__)
  #  define HEDLEY_TINYC_VERSION HEDLEY_VERSION_ENCODE(__TINYC__ / 1000, (__TINYC__ / 100) % 10, __TINYC__ % 100)
  #endif
  
  #if defined(HEDLEY_TINYC_VERSION_CHECK)
  #  undef HEDLEY_TINYC_VERSION_CHECK
  #endif
  #if defined(HEDLEY_TINYC_VERSION)
  #  define HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (HEDLEY_TINYC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch))
  #else
  #  define HEDLEY_TINYC_VERSION_CHECK(major,minor,patch) (0)
  #endif
  
  #if defined(HEDLEY_DMC_VERSION)
  #  undef HEDLEY_DMC_VERSION
  #endif
  #if defined(__DMC__)
  #  define HEDLEY_DMC_VERSION HEDLEY_VERSION_ENCODE(__DMC__ >> 8, (__DMC__ >> 4) & 0xf, __DMC__ & 0xf)
  #endif
  
  #if defined(HEDLEY_DMC_VERSION_CHECK)
  #  undef HEDLEY_DMC_VERSION_CHECK
  #endif
  #if defined(HEDLEY_DMC_VERSION)
  #  define HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (HEDLEY_DMC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch))
  #else
  #  define HEDLEY_DMC_VERSION_CHECK(major,minor,patch) (0)
  #endif
  
  #if defined(HEDLEY_COMPCERT_VERSION)
  #  undef HEDLEY_COMPCERT_VERSION
  #endif
  #if defined(__COMPCERT_VERSION__)
  #  define HEDLEY_COMPCERT_VERSION HEDLEY_VERSION_ENCODE(__COMPCERT_VERSION__ / 10000, (__COMPCERT_VERSION__ / 100) % 100, __COMPCERT_VERSION__ % 100)
  #endif
  
  #if defined(HEDLEY_COMPCERT_VERSION_CHECK)
  #  undef HEDLEY_COMPCERT_VERSION_CHECK
  #endif
  #if defined(HEDLEY_COMPCERT_VERSION)
  #  define HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (HEDLEY_COMPCERT_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch))
  #else
  #  define HEDLEY_COMPCERT_VERSION_CHECK(major,minor,patch) (0)
  #endif
  
  #if defined(HEDLEY_PELLES_VERSION)
  #  undef HEDLEY_PELLES_VERSION
  #endif
  #if defined(__POCC__)
  #  define HEDLEY_PELLES_VERSION HEDLEY_VERSION_ENCODE(__POCC__ / 100, __POCC__ % 100, 0)
  #endif
  
  #if defined(HEDLEY_PELLES_VERSION_CHECK)
  #  undef HEDLEY_PELLES_VERSION_CHECK
  #endif
  #if defined(HEDLEY_PELLES_VERSION)
  #  define HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (HEDLEY_PELLES_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch))
  #else
  #  define HEDLEY_PELLES_VERSION_CHECK(major,minor,patch) (0)
  #endif
  
  #if defined(HEDLEY_GCC_VERSION)
  #  undef HEDLEY_GCC_VERSION
  #endif
  #if \
    defined(HEDLEY_GNUC_VERSION) && \
    !defined(__clang__) && \
    !defined(HEDLEY_INTEL_VERSION) && \
    !defined(HEDLEY_PGI_VERSION) && \
    !defined(HEDLEY_ARM_VERSION) && \
    !defined(HEDLEY_TI_VERSION) && \
    !defined(__COMPCERT__)
  #  define HEDLEY_GCC_VERSION HEDLEY_GNUC_VERSION
  #endif
  
  #if defined(HEDLEY_GCC_VERSION_CHECK)
  #  undef HEDLEY_GCC_VERSION_CHECK
  #endif
  #if defined(HEDLEY_GCC_VERSION)
  #  define HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (HEDLEY_GCC_VERSION >= HEDLEY_VERSION_ENCODE(major, minor, patch))
  #else
  #  define HEDLEY_GCC_VERSION_CHECK(major,minor,patch) (0)
  #endif
  
  #if defined(HEDLEY_HAS_ATTRIBUTE)
  #  undef HEDLEY_HAS_ATTRIBUTE
  #endif
  #if defined(__has_attribute)
  #  define HEDLEY_HAS_ATTRIBUTE(attribute) __has_attribute(attribute)
  #else
  #  define HEDLEY_HAS_ATTRIBUTE(attribute) (0)
  #endif
  
  #if defined(HEDLEY_GNUC_HAS_ATTRIBUTE)
  #  undef HEDLEY_GNUC_HAS_ATTRIBUTE
  #endif
  #if defined(__has_attribute)
  #  define HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute)
  #else
  #  define HEDLEY_GNUC_HAS_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
  #endif
  
  #if defined(HEDLEY_GCC_HAS_ATTRIBUTE)
  #  undef HEDLEY_GCC_HAS_ATTRIBUTE
  #endif
  #if defined(__has_attribute)
  #  define HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) __has_attribute(attribute)
  #else
  #  define HEDLEY_GCC_HAS_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
  #endif
  
  #if defined(HEDLEY_HAS_CPP_ATTRIBUTE)
  #  undef HEDLEY_HAS_CPP_ATTRIBUTE
  #endif
  #if defined(__has_cpp_attribute) && defined(__cplusplus)
  #  define HEDLEY_HAS_CPP_ATTRIBUTE(attribute) __has_cpp_attribute(attribute)
  #else
  #  define HEDLEY_HAS_CPP_ATTRIBUTE(attribute) (0)
  #endif
  
  #if defined(HEDLEY_GNUC_HAS_CPP_ATTRIBUTE)
  #  undef HEDLEY_GNUC_HAS_CPP_ATTRIBUTE
  #endif
  #if defined(__has_cpp_attribute) && defined(__cplusplus)
  #  define HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)
  #else
  #  define HEDLEY_GNUC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
  #endif
  
  #if defined(HEDLEY_GCC_HAS_CPP_ATTRIBUTE)
  #  undef HEDLEY_GCC_HAS_CPP_ATTRIBUTE
  #endif
  #if defined(__has_cpp_attribute) && defined(__cplusplus)
  #  define HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) __has_cpp_attribute(attribute)
  #else
  #  define HEDLEY_GCC_HAS_CPP_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
  #endif
  
  #if defined(HEDLEY_HAS_BUILTIN)
  #  undef HEDLEY_HAS_BUILTIN
  #endif
  #if defined(__has_builtin)
  #  define HEDLEY_HAS_BUILTIN(builtin) __has_builtin(builtin)
  #else
  #  define HEDLEY_HAS_BUILTIN(builtin) (0)
  #endif
  
  #if defined(HEDLEY_GNUC_HAS_BUILTIN)
  #  undef HEDLEY_GNUC_HAS_BUILTIN
  #endif
  #if defined(__has_builtin)
  #  define HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)
  #else
  #  define HEDLEY_GNUC_HAS_BUILTIN(builtin,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
  #endif
  
  #if defined(HEDLEY_GCC_HAS_BUILTIN)
  #  undef HEDLEY_GCC_HAS_BUILTIN
  #endif
  #if defined(__has_builtin)
  #  define HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) __has_builtin(builtin)
  #else
  #  define HEDLEY_GCC_HAS_BUILTIN(builtin,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
  #endif
  
  #if defined(HEDLEY_HAS_FEATURE)
  #  undef HEDLEY_HAS_FEATURE
  #endif
  #if defined(__has_feature)
  #  define HEDLEY_HAS_FEATURE(feature) __has_feature(feature)
  #else
  #  define HEDLEY_HAS_FEATURE(feature) (0)
  #endif
  
  #if defined(HEDLEY_GNUC_HAS_FEATURE)
  #  undef HEDLEY_GNUC_HAS_FEATURE
  #endif
  #if defined(__has_feature)
  #  define HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)
  #else
  #  define HEDLEY_GNUC_HAS_FEATURE(feature,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
  #endif
  
  #if defined(HEDLEY_GCC_HAS_FEATURE)
  #  undef HEDLEY_GCC_HAS_FEATURE
  #endif
  #if defined(__has_feature)
  #  define HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) __has_feature(feature)
  #else
  #  define HEDLEY_GCC_HAS_FEATURE(feature,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
  #endif
  
  #if defined(HEDLEY_HAS_EXTENSION)
  #  undef HEDLEY_HAS_EXTENSION
  #endif
  #if defined(__has_extension)
  #  define HEDLEY_HAS_EXTENSION(extension) __has_extension(extension)
  #else
  #  define HEDLEY_HAS_EXTENSION(extension) (0)
  #endif
  
  #if defined(HEDLEY_GNUC_HAS_EXTENSION)
  #  undef HEDLEY_GNUC_HAS_EXTENSION
  #endif
  #if defined(__has_extension)
  #  define HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)
  #else
  #  define HEDLEY_GNUC_HAS_EXTENSION(extension,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
  #endif
  
  #if defined(HEDLEY_GCC_HAS_EXTENSION)
  #  undef HEDLEY_GCC_HAS_EXTENSION
  #endif
  #if defined(__has_extension)
  #  define HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) __has_extension(extension)
  #else
  #  define HEDLEY_GCC_HAS_EXTENSION(extension,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
  #endif
  
  #if defined(HEDLEY_HAS_DECLSPEC_ATTRIBUTE)
  #  undef HEDLEY_HAS_DECLSPEC_ATTRIBUTE
  #endif
  #if defined(__has_declspec_attribute)
  #  define HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) __has_declspec_attribute(attribute)
  #else
  #  define HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute) (0)
  #endif
  
  #if defined(HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE)
  #  undef HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE
  #endif
  #if defined(__has_declspec_attribute)
  #  define HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)
  #else
  #  define HEDLEY_GNUC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
  #endif
  
  #if defined(HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE)
  #  undef HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE
  #endif
  #if defined(__has_declspec_attribute)
  #  define HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) __has_declspec_attribute(attribute)
  #else
  #  define HEDLEY_GCC_HAS_DECLSPEC_ATTRIBUTE(attribute,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
  #endif
  
  #if defined(HEDLEY_HAS_WARNING)
  #  undef HEDLEY_HAS_WARNING
  #endif
  #if defined(__has_warning)
  #  define HEDLEY_HAS_WARNING(warning) __has_warning(warning)
  #else
  #  define HEDLEY_HAS_WARNING(warning) (0)
  #endif
  
  #if defined(HEDLEY_GNUC_HAS_WARNING)
  #  undef HEDLEY_GNUC_HAS_WARNING
  #endif
  #if defined(__has_warning)
  #  define HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)
  #else
  #  define HEDLEY_GNUC_HAS_WARNING(warning,major,minor,patch) HEDLEY_GNUC_VERSION_CHECK(major,minor,patch)
  #endif
  
  #if defined(HEDLEY_GCC_HAS_WARNING)
  #  undef HEDLEY_GCC_HAS_WARNING
  #endif
  #if defined(__has_warning)
  #  define HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) __has_warning(warning)
  #else
  #  define HEDLEY_GCC_HAS_WARNING(warning,major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
  #endif
  
  #if \
    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
    defined(__clang__) || \
    HEDLEY_GCC_VERSION_CHECK(3,0,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
    HEDLEY_IAR_VERSION_CHECK(8,0,0) || \
    HEDLEY_PGI_VERSION_CHECK(18,4,0) || \
    HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
    HEDLEY_TI_VERSION_CHECK(6,0,0) || \
    HEDLEY_CRAY_VERSION_CHECK(5,0,0) || \
    HEDLEY_TINYC_VERSION_CHECK(0,9,17) || \
    HEDLEY_SUNPRO_VERSION_CHECK(8,0,0) || \
    (HEDLEY_IBM_VERSION_CHECK(10,1,0) && defined(__C99_PRAGMA_OPERATOR))
  #  define HEDLEY_PRAGMA(value) _Pragma(#value)
  #elif HEDLEY_MSVC_VERSION_CHECK(15,0,0)
  #  define HEDLEY_PRAGMA(value) __pragma(value)
  #else
  #  define HEDLEY_PRAGMA(value)
  #endif
  
  #if defined(HEDLEY_DIAGNOSTIC_PUSH)
  #  undef HEDLEY_DIAGNOSTIC_PUSH
  #endif
  #if defined(HEDLEY_DIAGNOSTIC_POP)
  #  undef HEDLEY_DIAGNOSTIC_POP
  #endif
  #if defined(__clang__)
  #  define HEDLEY_DIAGNOSTIC_PUSH _Pragma("clang diagnostic push")
  #  define HEDLEY_DIAGNOSTIC_POP _Pragma("clang diagnostic pop")
  #elif HEDLEY_INTEL_VERSION_CHECK(13,0,0)
  #  define HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)")
  #  define HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)")
  #elif HEDLEY_GCC_VERSION_CHECK(4,6,0)
  #  define HEDLEY_DIAGNOSTIC_PUSH _Pragma("GCC diagnostic push")
  #  define HEDLEY_DIAGNOSTIC_POP _Pragma("GCC diagnostic pop")
  #elif HEDLEY_MSVC_VERSION_CHECK(15,0,0)
  #  define HEDLEY_DIAGNOSTIC_PUSH __pragma(warning(push))
  #  define HEDLEY_DIAGNOSTIC_POP __pragma(warning(pop))
  #elif HEDLEY_ARM_VERSION_CHECK(5,6,0)
  #  define HEDLEY_DIAGNOSTIC_PUSH _Pragma("push")
  #  define HEDLEY_DIAGNOSTIC_POP _Pragma("pop")
  #elif HEDLEY_TI_VERSION_CHECK(8,1,0)
  #  define HEDLEY_DIAGNOSTIC_PUSH _Pragma("diag_push")
  #  define HEDLEY_DIAGNOSTIC_POP _Pragma("diag_pop")
  #elif HEDLEY_PELLES_VERSION_CHECK(2,90,0)
  #  define HEDLEY_DIAGNOSTIC_PUSH _Pragma("warning(push)")
  #  define HEDLEY_DIAGNOSTIC_POP _Pragma("warning(pop)")
  #else
  #  define HEDLEY_DIAGNOSTIC_PUSH
  #  define HEDLEY_DIAGNOSTIC_POP
  #endif
  
  #if defined(HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED)
  #  undef HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
  #endif
  #if HEDLEY_HAS_WARNING("-Wdeprecated-declarations")
  #  define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("clang diagnostic ignored \"-Wdeprecated-declarations\"")
  #elif HEDLEY_INTEL_VERSION_CHECK(13,0,0)
  #  define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warning(disable:1478 1786)")
  #elif HEDLEY_PGI_VERSION_CHECK(17,10,0)
  #  define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1215,1444")
  #elif HEDLEY_GCC_VERSION_CHECK(4,3,0)
  #  define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"")
  #elif HEDLEY_MSVC_VERSION_CHECK(15,0,0)
  #  define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED __pragma(warning(disable:4996))
  #elif HEDLEY_TI_VERSION_CHECK(8,0,0)
  #  define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress 1291,1718")
  #elif HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && !defined(__cplusplus)
  #  define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,E_DEPRECATED_ATT,E_DEPRECATED_ATT_MESS)")
  #elif HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) && defined(__cplusplus)
  #  define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("error_messages(off,symdeprecated,symdeprecated2)")
  #elif HEDLEY_IAR_VERSION_CHECK(8,0,0)
  #  define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("diag_suppress=Pe1444,Pe1215")
  #elif HEDLEY_PELLES_VERSION_CHECK(2,90,0)
  #  define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED _Pragma("warn(disable:2241)")
  #else
  #  define HEDLEY_DIAGNOSTIC_DISABLE_DEPRECATED
  #endif
  
  #if defined(HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS)
  #  undef HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
  #endif
  #if HEDLEY_HAS_WARNING("-Wunknown-pragmas")
  #  define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("clang diagnostic ignored \"-Wunknown-pragmas\"")
  #elif HEDLEY_INTEL_VERSION_CHECK(13,0,0)
  #  define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("warning(disable:161)")
  #elif HEDLEY_PGI_VERSION_CHECK(17,10,0)
  #  define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 1675")
  #elif HEDLEY_GCC_VERSION_CHECK(4,3,0)
  #  define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("GCC diagnostic ignored \"-Wunknown-pragmas\"")
  #elif HEDLEY_MSVC_VERSION_CHECK(15,0,0)
  #  define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS __pragma(warning(disable:4068))
  #elif HEDLEY_TI_VERSION_CHECK(8,0,0)
  #  define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress 163")
  #elif HEDLEY_IAR_VERSION_CHECK(8,0,0)
  #  define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS _Pragma("diag_suppress=Pe161")
  #else
  #  define HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS
  #endif
  
  #if defined(HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL)
  #  undef HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
  #endif
  #if HEDLEY_HAS_WARNING("-Wcast-qual")
  #  define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("clang diagnostic ignored \"-Wcast-qual\"")
  #elif HEDLEY_INTEL_VERSION_CHECK(13,0,0)
  #  define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("warning(disable:2203 2331)")
  #elif HEDLEY_GCC_VERSION_CHECK(3,0,0)
  #  define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL _Pragma("GCC diagnostic ignored \"-Wcast-qual\"")
  #else
  #  define HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL
  #endif
  
  #if defined(HEDLEY_DEPRECATED)
  #  undef HEDLEY_DEPRECATED
  #endif
  #if defined(HEDLEY_DEPRECATED_FOR)
  #  undef HEDLEY_DEPRECATED_FOR
  #endif
  #if defined(__cplusplus) && (__cplusplus >= 201402L)
  #  define HEDLEY_DEPRECATED(since) [[deprecated("Since " #since)]]
  #  define HEDLEY_DEPRECATED_FOR(since, replacement) [[deprecated("Since " #since "; use " #replacement)]]
  #elif \
    HEDLEY_HAS_EXTENSION(attribute_deprecated_with_message) || \
    HEDLEY_GCC_VERSION_CHECK(4,5,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
    HEDLEY_ARM_VERSION_CHECK(5,6,0) || \
    HEDLEY_SUNPRO_VERSION_CHECK(5,13,0) || \
    HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
    HEDLEY_TI_VERSION_CHECK(8,3,0)
  #  define HEDLEY_DEPRECATED(since) __attribute__((__deprecated__("Since " #since)))
  #  define HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__("Since " #since "; use " #replacement)))
  #elif \
    HEDLEY_HAS_ATTRIBUTE(deprecated) || \
    HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
    HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
    HEDLEY_TI_VERSION_CHECK(8,0,0) || \
    (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))
  #  define HEDLEY_DEPRECATED(since) __attribute__((__deprecated__))
  #  define HEDLEY_DEPRECATED_FOR(since, replacement) __attribute__((__deprecated__))
  #elif HEDLEY_MSVC_VERSION_CHECK(14,0,0)
  #  define HEDLEY_DEPRECATED(since) __declspec(deprecated("Since " # since))
  #  define HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated("Since " #since "; use " #replacement))
  #elif \
    HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
    HEDLEY_PELLES_VERSION_CHECK(6,50,0)
  #  define HEDLEY_DEPRECATED(since) _declspec(deprecated)
  #  define HEDLEY_DEPRECATED_FOR(since, replacement) __declspec(deprecated)
  #elif HEDLEY_IAR_VERSION_CHECK(8,0,0)
  #  define HEDLEY_DEPRECATED(since) _Pragma("deprecated")
  #  define HEDLEY_DEPRECATED_FOR(since, replacement) _Pragma("deprecated")
  #else
  #  define HEDLEY_DEPRECATED(since)
  #  define HEDLEY_DEPRECATED_FOR(since, replacement)
  #endif
  
  #if defined(HEDLEY_UNAVAILABLE)
  #  undef HEDLEY_UNAVAILABLE
  #endif
  #if \
    HEDLEY_HAS_ATTRIBUTE(warning) || \
    HEDLEY_GCC_VERSION_CHECK(4,3,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0)
  #  define HEDLEY_UNAVAILABLE(available_since) __attribute__((__warning__("Not available until " #available_since)))
  #else
  #  define HEDLEY_UNAVAILABLE(available_since)
  #endif
  
  #if defined(HEDLEY_WARN_UNUSED_RESULT)
  #  undef HEDLEY_WARN_UNUSED_RESULT
  #endif
  #if defined(__cplusplus) && (__cplusplus >= 201703L)
  #  define HEDLEY_WARN_UNUSED_RESULT [[nodiscard]]
  #elif \
    HEDLEY_HAS_ATTRIBUTE(warn_unused_result) || \
    HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
    HEDLEY_TI_VERSION_CHECK(8,0,0) || \
    (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
    (HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \
    HEDLEY_PGI_VERSION_CHECK(17,10,0)
  #  define HEDLEY_WARN_UNUSED_RESULT __attribute__((__warn_unused_result__))
  #elif defined(_Check_return_) /* SAL */
  #  define HEDLEY_WARN_UNUSED_RESULT _Check_return_
  #else
  #  define HEDLEY_WARN_UNUSED_RESULT
  #endif
  
  #if defined(HEDLEY_SENTINEL)
  #  undef HEDLEY_SENTINEL
  #endif
  #if \
    HEDLEY_HAS_ATTRIBUTE(sentinel) || \
    HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
    HEDLEY_ARM_VERSION_CHECK(5,4,0)
  #  define HEDLEY_SENTINEL(position) __attribute__((__sentinel__(position)))
  #else
  #  define HEDLEY_SENTINEL(position)
  #endif
  
  #if defined(HEDLEY_NO_RETURN)
  #  undef HEDLEY_NO_RETURN
  #endif
  #if HEDLEY_IAR_VERSION_CHECK(8,0,0)
  #  define HEDLEY_NO_RETURN __noreturn
  #elif HEDLEY_INTEL_VERSION_CHECK(13,0,0)
  #  define HEDLEY_NO_RETURN __attribute__((__noreturn__))
  #elif defined(__STDC_VERSION__) && __STDC_VERSION__ >= 201112L
  #  define HEDLEY_NO_RETURN _Noreturn
  #elif defined(__cplusplus) && (__cplusplus >= 201103L)
  #  define HEDLEY_NO_RETURN [[noreturn]]
  #elif \
    HEDLEY_HAS_ATTRIBUTE(noreturn) || \
    HEDLEY_GCC_VERSION_CHECK(3,2,0) || \
    HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
    HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
    HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
    HEDLEY_TI_VERSION_CHECK(18,0,0) || \
    (HEDLEY_TI_VERSION_CHECK(17,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))
  #  define HEDLEY_NO_RETURN __attribute__((__noreturn__))
  #elif HEDLEY_MSVC_VERSION_CHECK(13,10,0)
  #  define HEDLEY_NO_RETURN __declspec(noreturn)
  #elif HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus)
  #  define HEDLEY_NO_RETURN _Pragma("FUNC_NEVER_RETURNS;")
  #elif HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)
  #  define HEDLEY_NO_RETURN __attribute((noreturn))
  #elif HEDLEY_PELLES_VERSION_CHECK(9,0,0)
  #  define HEDLEY_NO_RETURN __declspec(noreturn)
  #else
  #  define HEDLEY_NO_RETURN
  #endif
  
  #if defined(HEDLEY_UNREACHABLE)
  #  undef HEDLEY_UNREACHABLE
  #endif
  #if defined(HEDLEY_UNREACHABLE_RETURN)
  #  undef HEDLEY_UNREACHABLE_RETURN
  #endif
  #if \
    (HEDLEY_HAS_BUILTIN(__builtin_unreachable) && (!defined(HEDLEY_ARM_VERSION))) || \
    HEDLEY_GCC_VERSION_CHECK(4,5,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
    HEDLEY_IBM_VERSION_CHECK(13,1,5)
  #  define HEDLEY_UNREACHABLE() __builtin_unreachable()
  #elif HEDLEY_MSVC_VERSION_CHECK(13,10,0)
  #  define HEDLEY_UNREACHABLE() __assume(0)
  #elif HEDLEY_TI_VERSION_CHECK(6,0,0)
  #  if defined(__cplusplus)
  #    define HEDLEY_UNREACHABLE() std::_nassert(0)
  #  else
  #    define HEDLEY_UNREACHABLE() _nassert(0)
  #  endif
  #  define HEDLEY_UNREACHABLE_RETURN(value) return value
  #elif defined(EXIT_FAILURE)
  #  define HEDLEY_UNREACHABLE() abort()
  #else
  #  define HEDLEY_UNREACHABLE()
  #  define HEDLEY_UNREACHABLE_RETURN(value) return value
  #endif
  #if !defined(HEDLEY_UNREACHABLE_RETURN)
  #  define HEDLEY_UNREACHABLE_RETURN(value) HEDLEY_UNREACHABLE()
  #endif
  
  #if defined(HEDLEY_ASSUME)
  #  undef HEDLEY_ASSUME
  #endif
  #if \
    HEDLEY_MSVC_VERSION_CHECK(13,10,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0)
  #  define HEDLEY_ASSUME(expr) __assume(expr)
  #elif HEDLEY_HAS_BUILTIN(__builtin_assume)
  #  define HEDLEY_ASSUME(expr) __builtin_assume(expr)
  #elif HEDLEY_TI_VERSION_CHECK(6,0,0)
  #  if defined(__cplusplus)
  #    define HEDLEY_ASSUME(expr) std::_nassert(expr)
  #  else
  #    define HEDLEY_ASSUME(expr) _nassert(expr)
  #  endif
  #elif \
    (HEDLEY_HAS_BUILTIN(__builtin_unreachable) && !defined(HEDLEY_ARM_VERSION)) || \
    HEDLEY_GCC_VERSION_CHECK(4,5,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
    HEDLEY_IBM_VERSION_CHECK(13,1,5)
  #  define HEDLEY_ASSUME(expr) ((void) ((expr) ? 1 : (__builtin_unreachable(), 1)))
  #else
  #  define HEDLEY_ASSUME(expr) ((void) (expr))
  #endif
  
  
  HEDLEY_DIAGNOSTIC_PUSH
  #if \
    HEDLEY_HAS_WARNING("-Wvariadic-macros") || \
    HEDLEY_GCC_VERSION_CHECK(4,0,0)
  #  if defined(__clang__)
  #    pragma clang diagnostic ignored "-Wvariadic-macros"
  #  elif defined(HEDLEY_GCC_VERSION)
  #    pragma GCC diagnostic ignored "-Wvariadic-macros"
  #  endif
  #endif
  #if defined(HEDLEY_NON_NULL)
  #  undef HEDLEY_NON_NULL
  #endif
  #if \
    HEDLEY_HAS_ATTRIBUTE(nonnull) || \
    HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
    HEDLEY_ARM_VERSION_CHECK(4,1,0)
  #  define HEDLEY_NON_NULL(...) __attribute__((__nonnull__(__VA_ARGS__)))
  #else
  #  define HEDLEY_NON_NULL(...)
  #endif
  HEDLEY_DIAGNOSTIC_POP
  
  #if defined(HEDLEY_PRINTF_FORMAT)
  #  undef HEDLEY_PRINTF_FORMAT
  #endif
  #if defined(__MINGW32__) && HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && !defined(__USE_MINGW_ANSI_STDIO)
  #  define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(ms_printf, string_idx, first_to_check)))
  #elif defined(__MINGW32__) && HEDLEY_GCC_HAS_ATTRIBUTE(format,4,4,0) && defined(__USE_MINGW_ANSI_STDIO)
  #  define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(gnu_printf, string_idx, first_to_check)))
  #elif \
    HEDLEY_HAS_ATTRIBUTE(format) || \
    HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
    HEDLEY_ARM_VERSION_CHECK(5,6,0) || \
    HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
    HEDLEY_TI_VERSION_CHECK(8,0,0) || \
    (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))
  #  define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __attribute__((__format__(__printf__, string_idx, first_to_check)))
  #elif HEDLEY_PELLES_VERSION_CHECK(6,0,0)
  #  define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check) __declspec(vaformat(printf,string_idx,first_to_check))
  #else
  #  define HEDLEY_PRINTF_FORMAT(string_idx,first_to_check)
  #endif
  
  #if defined(HEDLEY_CONSTEXPR)
  #  undef HEDLEY_CONSTEXPR
  #endif
  #if defined(__cplusplus)
  #  if __cplusplus >= 201103L
  #    define HEDLEY_CONSTEXPR constexpr
  #  endif
  #endif
  #if !defined(HEDLEY_CONSTEXPR)
  #  define HEDLEY_CONSTEXPR
  #endif
  
  #if defined(HEDLEY_PREDICT)
  #  undef HEDLEY_PREDICT
  #endif
  #if defined(HEDLEY_LIKELY)
  #  undef HEDLEY_LIKELY
  #endif
  #if defined(HEDLEY_UNLIKELY)
  #  undef HEDLEY_UNLIKELY
  #endif
  #if defined(HEDLEY_UNPREDICTABLE)
  #  undef HEDLEY_UNPREDICTABLE
  #endif
  #if HEDLEY_HAS_BUILTIN(__builtin_unpredictable)
  #  define HEDLEY_UNPREDICTABLE(expr) __builtin_unpredictable(!!(expr))
  #endif
  #if \
    HEDLEY_HAS_BUILTIN(__builtin_expect_with_probability) || \
    HEDLEY_GCC_VERSION_CHECK(9,0,0)
  #  define HEDLEY_PREDICT(expr, value, probability) __builtin_expect_with_probability(expr, value, probability)
  #  define HEDLEY_PREDICT_TRUE(expr, probability) __builtin_expect_with_probability(!!(expr), 1, probability)
  #  define HEDLEY_PREDICT_FALSE(expr, probability) __builtin_expect_with_probability(!!(expr), 0, probability)
  #  define HEDLEY_LIKELY(expr) __builtin_expect(!!(expr), 1)
  #  define HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0)
  #  if !defined(HEDLEY_BUILTIN_UNPREDICTABLE)
  #    define HEDLEY_BUILTIN_UNPREDICTABLE(expr) __builtin_expect_with_probability(!!(expr), 1, 0.5)
  #  endif
  #elif \
    HEDLEY_HAS_BUILTIN(__builtin_expect) || \
    HEDLEY_GCC_VERSION_CHECK(3,0,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
    (HEDLEY_SUNPRO_VERSION_CHECK(5,15,0) && defined(__cplusplus)) || \
    HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
    HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
    HEDLEY_TI_VERSION_CHECK(6,1,0) || \
    HEDLEY_TINYC_VERSION_CHECK(0,9,27)
  #  define HEDLEY_PREDICT(expr, expected, probability) \
    (((probability) >= 0.9) ? __builtin_expect(!!(expr), (expected)) : (((void) (expected)), !!(expr)))
  #  define HEDLEY_PREDICT_TRUE(expr, probability) \
       (__extension__ ({ \
         HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \
         ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 1) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 0) : !!(expr))); \
       }))
  #  define HEDLEY_PREDICT_FALSE(expr, probability) \
       (__extension__ ({ \
         HEDLEY_CONSTEXPR double hedley_probability_ = (probability); \
         ((hedley_probability_ >= 0.9) ? __builtin_expect(!!(expr), 0) : ((hedley_probability_ <= 0.1) ? __builtin_expect(!!(expr), 1) : !!(expr))); \
       }))
  #  define HEDLEY_LIKELY(expr)   __builtin_expect(!!(expr), 1)
  #  define HEDLEY_UNLIKELY(expr) __builtin_expect(!!(expr), 0)
  #else
  #  define HEDLEY_PREDICT(expr, expected, probability) (((void) (expected)), !!(expr))
  #  define HEDLEY_PREDICT_TRUE(expr, probability) (!!(expr))
  #  define HEDLEY_PREDICT_FALSE(expr, probability) (!!(expr))
  #  define HEDLEY_LIKELY(expr) (!!(expr))
  #  define HEDLEY_UNLIKELY(expr) (!!(expr))
  #endif
  #if !defined(HEDLEY_UNPREDICTABLE)
  #  define HEDLEY_UNPREDICTABLE(expr) HEDLEY_PREDICT(expr, 1, 0.5)
  #endif
  
  #if defined(HEDLEY_MALLOC)
  #  undef HEDLEY_MALLOC
  #endif
  #if \
    HEDLEY_HAS_ATTRIBUTE(malloc) || \
    HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
    HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
    HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
    HEDLEY_IBM_VERSION_CHECK(12,1,0) || \
    HEDLEY_TI_VERSION_CHECK(8,0,0) || \
    (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))
  #  define HEDLEY_MALLOC __attribute__((__malloc__))
  #elif HEDLEY_MSVC_VERSION_CHECK(14, 0, 0)
  #  define HEDLEY_MALLOC __declspec(restrict)
  #else
  #  define HEDLEY_MALLOC
  #endif
  
  #if defined(HEDLEY_PURE)
  #  undef HEDLEY_PURE
  #endif
  #if \
    HEDLEY_HAS_ATTRIBUTE(pure) || \
    HEDLEY_GCC_VERSION_CHECK(2,96,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
    HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
    HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
    HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
    HEDLEY_TI_VERSION_CHECK(8,0,0) || \
    (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
    HEDLEY_PGI_VERSION_CHECK(17,10,0)
  #  define HEDLEY_PURE __attribute__((__pure__))
  #elif HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus)
  #  define HEDLEY_PURE _Pragma("FUNC_IS_PURE;")
  #else
  #  define HEDLEY_PURE
  #endif
  
  #if defined(HEDLEY_CONST)
  #  undef HEDLEY_CONST
  #endif
  #if \
    HEDLEY_HAS_ATTRIBUTE(const) || \
    HEDLEY_GCC_VERSION_CHECK(2,5,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
    HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
    HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
    HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
    HEDLEY_TI_VERSION_CHECK(8,0,0) || \
    (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__)) || \
    HEDLEY_PGI_VERSION_CHECK(17,10,0)
  #  define HEDLEY_CONST __attribute__((__const__))
  #else
  #  define HEDLEY_CONST HEDLEY_PURE
  #endif
  
  #if defined(HEDLEY_RESTRICT)
  #  undef HEDLEY_RESTRICT
  #endif
  #if defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && !defined(__cplusplus)
  #  define HEDLEY_RESTRICT restrict
  #elif \
    HEDLEY_GCC_VERSION_CHECK(3,1,0) || \
    HEDLEY_MSVC_VERSION_CHECK(14,0,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
    HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
    HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
    HEDLEY_PGI_VERSION_CHECK(17,10,0) || \
    HEDLEY_TI_VERSION_CHECK(8,0,0) || \
    (HEDLEY_SUNPRO_VERSION_CHECK(5,14,0) && defined(__cplusplus)) || \
    HEDLEY_IAR_VERSION_CHECK(8,0,0) || \
    defined(__clang__)
  #  define HEDLEY_RESTRICT __restrict
  #elif HEDLEY_SUNPRO_VERSION_CHECK(5,3,0) && !defined(__cplusplus)
  #  define HEDLEY_RESTRICT _Restrict
  #else
  #  define HEDLEY_RESTRICT
  #endif
  
  #if defined(HEDLEY_INLINE)
  #  undef HEDLEY_INLINE
  #endif
  #if \
    (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L)) || \
    (defined(__cplusplus) && (__cplusplus >= 199711L))
  #  define HEDLEY_INLINE inline
  #elif \
    defined(HEDLEY_GCC_VERSION) || \
    HEDLEY_ARM_VERSION_CHECK(6,2,0)
  #  define HEDLEY_INLINE __inline__
  #elif \
    HEDLEY_MSVC_VERSION_CHECK(12,0,0) || \
    HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
    HEDLEY_TI_VERSION_CHECK(8,0,0)
  #  define HEDLEY_INLINE __inline
  #else
  #  define HEDLEY_INLINE
  #endif
  
  #if defined(HEDLEY_ALWAYS_INLINE)
  #  undef HEDLEY_ALWAYS_INLINE
  #endif
  #if \
    HEDLEY_HAS_ATTRIBUTE(always_inline) || \
    HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
    HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
    HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
    HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
    HEDLEY_TI_VERSION_CHECK(8,0,0) || \
    (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))
  #  define HEDLEY_ALWAYS_INLINE __attribute__((__always_inline__)) HEDLEY_INLINE
  #elif HEDLEY_MSVC_VERSION_CHECK(12,0,0)
  #  define HEDLEY_ALWAYS_INLINE __forceinline
  #elif HEDLEY_TI_VERSION_CHECK(7,0,0) && defined(__cplusplus)
  #  define HEDLEY_ALWAYS_INLINE _Pragma("FUNC_ALWAYS_INLINE;")
  #elif HEDLEY_IAR_VERSION_CHECK(8,0,0)
  #  define HEDLEY_ALWAYS_INLINE _Pragma("inline=forced")
  #else
  #  define HEDLEY_ALWAYS_INLINE HEDLEY_INLINE
  #endif
  
  #if defined(HEDLEY_NEVER_INLINE)
  #  undef HEDLEY_NEVER_INLINE
  #endif
  #if \
    HEDLEY_HAS_ATTRIBUTE(noinline) || \
    HEDLEY_GCC_VERSION_CHECK(4,0,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
    HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
    HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
    HEDLEY_IBM_VERSION_CHECK(10,1,0) || \
    HEDLEY_TI_VERSION_CHECK(8,0,0) || \
    (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))
  #  define HEDLEY_NEVER_INLINE __attribute__((__noinline__))
  #elif HEDLEY_MSVC_VERSION_CHECK(13,10,0)
  #  define HEDLEY_NEVER_INLINE __declspec(noinline)
  #elif HEDLEY_PGI_VERSION_CHECK(10,2,0)
  #  define HEDLEY_NEVER_INLINE _Pragma("noinline")
  #elif HEDLEY_TI_VERSION_CHECK(6,0,0) && defined(__cplusplus)
  #  define HEDLEY_NEVER_INLINE _Pragma("FUNC_CANNOT_INLINE;")
  #elif HEDLEY_IAR_VERSION_CHECK(8,0,0)
  #  define HEDLEY_NEVER_INLINE _Pragma("inline=never")
  #elif HEDLEY_COMPCERT_VERSION_CHECK(3,2,0)
  #  define HEDLEY_NEVER_INLINE __attribute((noinline))
  #elif HEDLEY_PELLES_VERSION_CHECK(9,0,0)
  #  define HEDLEY_NEVER_INLINE __declspec(noinline)
  #else
  #  define HEDLEY_NEVER_INLINE
  #endif
  
  #if defined(HEDLEY_PRIVATE)
  #  undef HEDLEY_PRIVATE
  #endif
  #if defined(HEDLEY_PUBLIC)
  #  undef HEDLEY_PUBLIC
  #endif
  #if defined(HEDLEY_IMPORT)
  #  undef HEDLEY_IMPORT
  #endif
  #if defined(_WIN32) || defined(__CYGWIN__)
  #  define HEDLEY_PRIVATE
  #  define HEDLEY_PUBLIC   __declspec(dllexport)
  #  define HEDLEY_IMPORT   __declspec(dllimport)
  #else
  #  if \
      HEDLEY_HAS_ATTRIBUTE(visibility) || \
      HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
      HEDLEY_SUNPRO_VERSION_CHECK(5,11,0) || \
      HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
      HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
      HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
      HEDLEY_TI_VERSION_CHECK(8,0,0) || \
      (HEDLEY_TI_VERSION_CHECK(7,3,0) && defined(__TI_EABI__) && defined(__TI_GNU_ATTRIBUTE_SUPPORT__))
  #    define HEDLEY_PRIVATE __attribute__((__visibility__("hidden")))
  #    define HEDLEY_PUBLIC  __attribute__((__visibility__("default")))
  #  else
  #    define HEDLEY_PRIVATE
  #    define HEDLEY_PUBLIC
  #  endif
  #  define HEDLEY_IMPORT    extern
  #endif
  
  #if defined(HEDLEY_NO_THROW)
  #  undef HEDLEY_NO_THROW
  #endif
  #if \
    HEDLEY_HAS_ATTRIBUTE(nothrow) || \
    HEDLEY_GCC_VERSION_CHECK(3,3,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0)
  #  define HEDLEY_NO_THROW __attribute__((__nothrow__))
  #elif \
    HEDLEY_MSVC_VERSION_CHECK(13,1,0) || \
    HEDLEY_ARM_VERSION_CHECK(4,1,0)
  #  define HEDLEY_NO_THROW __declspec(nothrow)
  #else
  #  define HEDLEY_NO_THROW
  #endif
  
  #if defined(HEDLEY_FALL_THROUGH)
  #  undef HEDLEY_FALL_THROUGH
  #endif
  #if \
       defined(__cplusplus) && \
       (!defined(HEDLEY_SUNPRO_VERSION) || HEDLEY_SUNPRO_VERSION_CHECK(5,15,0)) && \
       !defined(HEDLEY_PGI_VERSION)
  #  if \
       (__cplusplus >= 201703L) || \
       ((__cplusplus >= 201103L) && HEDLEY_HAS_CPP_ATTRIBUTE(fallthrough))
  #    define HEDLEY_FALL_THROUGH [[fallthrough]]
  #  elif (__cplusplus >= 201103L) && HEDLEY_HAS_CPP_ATTRIBUTE(clang::fallthrough)
  #    define HEDLEY_FALL_THROUGH [[clang::fallthrough]]
  #  elif (__cplusplus >= 201103L) && HEDLEY_GCC_VERSION_CHECK(7,0,0)
  #    define HEDLEY_FALL_THROUGH [[gnu::fallthrough]]
  #  endif
  #endif
  #if !defined(HEDLEY_FALL_THROUGH)
  #  if HEDLEY_GNUC_HAS_ATTRIBUTE(fallthrough,7,0,0) && !defined(HEDLEY_PGI_VERSION)
  #    define HEDLEY_FALL_THROUGH __attribute__((__fallthrough__))
  #  elif defined(__fallthrough) /* SAL */
  #    define HEDLEY_FALL_THROUGH __fallthrough
  #  else
  #    define HEDLEY_FALL_THROUGH
  #  endif
  #endif
  
  #if defined(HEDLEY_RETURNS_NON_NULL)
  #  undef HEDLEY_RETURNS_NON_NULL
  #endif
  #if \
    HEDLEY_HAS_ATTRIBUTE(returns_nonnull) || \
    HEDLEY_GCC_VERSION_CHECK(4,9,0)
  #  define HEDLEY_RETURNS_NON_NULL __attribute__((__returns_nonnull__))
  #elif defined(_Ret_notnull_) /* SAL */
  #  define HEDLEY_RETURNS_NON_NULL _Ret_notnull_
  #else
  #  define HEDLEY_RETURNS_NON_NULL
  #endif
  
  #if defined(HEDLEY_ARRAY_PARAM)
  #  undef HEDLEY_ARRAY_PARAM
  #endif
  #if \
    defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) && \
    !defined(__STDC_NO_VLA__) && \
    !defined(__cplusplus) && \
    !defined(HEDLEY_PGI_VERSION) && \
    !defined(HEDLEY_TINYC_VERSION)
  #  define HEDLEY_ARRAY_PARAM(name) (name)
  #else
  #  define HEDLEY_ARRAY_PARAM(name)
  #endif
  
  #if defined(HEDLEY_IS_CONSTANT)
  #  undef HEDLEY_IS_CONSTANT
  #endif
  #if defined(HEDLEY_REQUIRE_CONSTEXPR)
  #  undef HEDLEY_REQUIRE_CONSTEXPR
  #endif
  /* Note the double-underscore. For internal use only; no API
   * guarantees! */
  #if defined(HEDLEY__IS_CONSTEXPR)
  #  undef HEDLEY__IS_CONSTEXPR
  #endif
  
  #if \
    HEDLEY_HAS_BUILTIN(__builtin_constant_p) || \
    HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
    HEDLEY_TINYC_VERSION_CHECK(0,9,19) || \
    HEDLEY_ARM_VERSION_CHECK(4,1,0) || \
    HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
    HEDLEY_TI_VERSION_CHECK(6,1,0) || \
    HEDLEY_SUNPRO_VERSION_CHECK(5,10,0) || \
    HEDLEY_CRAY_VERSION_CHECK(8,1,0)
  #  define HEDLEY_IS_CONSTANT(expr) __builtin_constant_p(expr)
  #endif
  #if !defined(__cplusplus)
  #  if \
         HEDLEY_HAS_BUILTIN(__builtin_types_compatible_p) || \
         HEDLEY_GCC_VERSION_CHECK(3,4,0) || \
         HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
         HEDLEY_IBM_VERSION_CHECK(13,1,0) || \
         HEDLEY_CRAY_VERSION_CHECK(8,1,0) || \
         HEDLEY_ARM_VERSION_CHECK(5,4,0) || \
         HEDLEY_TINYC_VERSION_CHECK(0,9,24)
  #    if defined(__INTPTR_TYPE__)
  #      define HEDLEY__IS_CONSTEXPR(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0)), int*)
  #    else
  #      include <stdint.h>
  #      define HEDLEY__IS_CONSTEXPR(expr) __builtin_types_compatible_p(__typeof__((1 ? (void*) ((intptr_t) ((expr) * 0)) : (int*) 0)), int*)
  #    endif
  #  elif \
         (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L) && !defined(HEDLEY_SUNPRO_VERSION) && !defined(HEDLEY_PGI_VERSION)) || \
         HEDLEY_HAS_EXTENSION(c_generic_selections) || \
         HEDLEY_GCC_VERSION_CHECK(4,9,0) || \
         HEDLEY_INTEL_VERSION_CHECK(17,0,0) || \
         HEDLEY_IBM_VERSION_CHECK(12,1,0) || \
         HEDLEY_ARM_VERSION_CHECK(5,3,0)
  #    if defined(__INTPTR_TYPE__)
  #      define HEDLEY__IS_CONSTEXPR(expr) _Generic((1 ? (void*) ((__INTPTR_TYPE__) ((expr) * 0)) : (int*) 0), int*: 1, void*: 0)
  #    else
  #      include <stdint.h>
  #      define HEDLEY__IS_CONSTEXPR(expr) _Generic((1 ? (void*) ((intptr_t) * 0) : (int*) 0), int*: 1, void*: 0)
  #    endif
  #  elif \
         defined(HEDLEY_GCC_VERSION) || \
         defined(HEDLEY_INTEL_VERSION) || \
         defined(HEDLEY_TINYC_VERSION) || \
         defined(HEDLEY_TI_VERSION) || \
         defined(__clang__)
  #    define HEDLEY__IS_CONSTEXPR(expr) ( \
           sizeof(void) != \
           sizeof(*( \
             1 ? \
               ((void*) ((expr) * 0L) ) : \
               ((struct { char v[sizeof(void) * 2]; } *) 1) \
             ) \
           ) \
         )
  #  endif
  #endif
  #if defined(HEDLEY__IS_CONSTEXPR)
  #  if !defined(HEDLEY_IS_CONSTANT)
  #    define HEDLEY_IS_CONSTANT(expr) HEDLEY__IS_CONSTEXPR(expr)
  #  endif
  #  define HEDLEY_REQUIRE_CONSTEXPR(expr) (HEDLEY__IS_CONSTEXPR(expr) ? (expr) : (-1))
  #else
  #  if !defined(HEDLEY_IS_CONSTANT)
  #    define HEDLEY_IS_CONSTANT(expr) (0)
  #  endif
  #  define HEDLEY_REQUIRE_CONSTEXPR(expr) (expr)
  #endif
  
  #if defined(HEDLEY_BEGIN_C_DECLS)
  #  undef HEDLEY_BEGIN_C_DECLS
  #endif
  #if defined(HEDLEY_END_C_DECLS)
  #  undef HEDLEY_END_C_DECLS
  #endif
  #if defined(HEDLEY_C_DECL)
  #  undef HEDLEY_C_DECL
  #endif
  #if defined(__cplusplus)
  #  define HEDLEY_BEGIN_C_DECLS extern "C" {
  #  define HEDLEY_END_C_DECLS }
  #  define HEDLEY_C_DECL extern "C"
  #else
  #  define HEDLEY_BEGIN_C_DECLS
  #  define HEDLEY_END_C_DECLS
  #  define HEDLEY_C_DECL
  #endif
  
  #if defined(HEDLEY_STATIC_ASSERT)
  #  undef HEDLEY_STATIC_ASSERT
  #endif
  #if \
    !defined(__cplusplus) && ( \
        (defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 201112L)) || \
        HEDLEY_HAS_FEATURE(c_static_assert) || \
        HEDLEY_GCC_VERSION_CHECK(6,0,0) || \
        HEDLEY_INTEL_VERSION_CHECK(13,0,0) || \
        defined(_Static_assert) \
      )
  #  define HEDLEY_STATIC_ASSERT(expr, message) _Static_assert(expr, message)
  #elif \
    (defined(__cplusplus) && (__cplusplus >= 201703L)) || \
    HEDLEY_MSVC_VERSION_CHECK(16,0,0) || \
    (defined(__cplusplus) && HEDLEY_TI_VERSION_CHECK(8,3,0))
  #  define HEDLEY_STATIC_ASSERT(expr, message) static_assert(expr, message)
  #elif defined(__cplusplus) && (__cplusplus >= 201103L)
  #  define HEDLEY_STATIC_ASSERT(expr, message) static_assert(expr)
  #else
  #  define HEDLEY_STATIC_ASSERT(expr, message)
  #endif
  
  #if defined(HEDLEY_CONST_CAST)
  #  undef HEDLEY_CONST_CAST
  #endif
  #if defined(__cplusplus)
  #  define HEDLEY_CONST_CAST(T, expr) (const_cast<T>(expr))
  #elif \
    HEDLEY_HAS_WARNING("-Wcast-qual") || \
    HEDLEY_GCC_VERSION_CHECK(4,6,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0)
  #  define HEDLEY_CONST_CAST(T, expr) (__extension__ ({ \
        HEDLEY_DIAGNOSTIC_PUSH \
        HEDLEY_DIAGNOSTIC_DISABLE_CAST_QUAL \
        ((T) (expr)); \
        HEDLEY_DIAGNOSTIC_POP \
      }))
  #else
  #  define HEDLEY_CONST_CAST(T, expr) ((T) (expr))
  #endif
  
  #if defined(HEDLEY_REINTERPRET_CAST)
  #  undef HEDLEY_REINTERPRET_CAST
  #endif
  #if defined(__cplusplus)
  #  define HEDLEY_REINTERPRET_CAST(T, expr) (reinterpret_cast<T>(expr))
  #else
  #  define HEDLEY_REINTERPRET_CAST(T, expr) (*((T*) &(expr)))
  #endif
  
  #if defined(HEDLEY_STATIC_CAST)
  #  undef HEDLEY_STATIC_CAST
  #endif
  #if defined(__cplusplus)
  #  define HEDLEY_STATIC_CAST(T, expr) (static_cast<T>(expr))
  #else
  #  define HEDLEY_STATIC_CAST(T, expr) ((T) (expr))
  #endif
  
  #if defined(HEDLEY_CPP_CAST)
  #  undef HEDLEY_CPP_CAST
  #endif
  #if defined(__cplusplus)
  #  define HEDLEY_CPP_CAST(T, expr) static_cast<T>(expr)
  #else
  #  define HEDLEY_CPP_CAST(T, expr) (expr)
  #endif
  
  #if defined(HEDLEY_MESSAGE)
  #  undef HEDLEY_MESSAGE
  #endif
  #if HEDLEY_HAS_WARNING("-Wunknown-pragmas")
  #  define HEDLEY_MESSAGE(msg) \
    HEDLEY_DIAGNOSTIC_PUSH \
    HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \
    HEDLEY_PRAGMA(message msg) \
    HEDLEY_DIAGNOSTIC_POP
  #elif \
    HEDLEY_GCC_VERSION_CHECK(4,4,0) || \
    HEDLEY_INTEL_VERSION_CHECK(13,0,0)
  #  define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(message msg)
  #elif HEDLEY_CRAY_VERSION_CHECK(5,0,0)
  #  define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(_CRI message msg)
  #elif HEDLEY_IAR_VERSION_CHECK(8,0,0)
  #  define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(message(msg))
  #elif HEDLEY_PELLES_VERSION_CHECK(2,0,0)
  #  define HEDLEY_MESSAGE(msg) HEDLEY_PRAGMA(message(msg))
  #else
  #  define HEDLEY_MESSAGE(msg)
  #endif
  
  #if defined(HEDLEY_WARNING)
  #  undef HEDLEY_WARNING
  #endif
  #if HEDLEY_HAS_WARNING("-Wunknown-pragmas")
  #  define HEDLEY_WARNING(msg) \
    HEDLEY_DIAGNOSTIC_PUSH \
    HEDLEY_DIAGNOSTIC_DISABLE_UNKNOWN_PRAGMAS \
    HEDLEY_PRAGMA(clang warning msg) \
    HEDLEY_DIAGNOSTIC_POP
  #elif \
    HEDLEY_GCC_VERSION_CHECK(4,8,0) || \
    HEDLEY_PGI_VERSION_CHECK(18,4,0)
  #  define HEDLEY_WARNING(msg) HEDLEY_PRAGMA(GCC warning msg)
  #elif HEDLEY_MSVC_VERSION_CHECK(15,0,0)
  #  define HEDLEY_WARNING(msg) HEDLEY_PRAGMA(message(msg))
  #else
  #  define HEDLEY_WARNING(msg) HEDLEY_MESSAGE(msg)
  #endif
  
  #if defined(HEDLEY_REQUIRE_MSG)
  #  undef HEDLEY_REQUIRE_MSG
  #endif
  #if HEDLEY_HAS_ATTRIBUTE(diagnose_if)
  #  if HEDLEY_HAS_WARNING("-Wgcc-compat")
  #    define HEDLEY_REQUIRE_MSG(expr, msg) \
    HEDLEY_DIAGNOSTIC_PUSH \
    _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
    __attribute__((__diagnose_if__(!(expr), msg, "error"))) \
    HEDLEY_DIAGNOSTIC_POP
  #  else
  #    define HEDLEY_REQUIRE_MSG(expr, msg) __attribute__((__diagnose_if__(!(expr), msg, "error")))
  #  endif
  #else
  #  define HEDLEY_REQUIRE_MSG(expr, msg)
  #endif
  
  #if defined(HEDLEY_REQUIRE)
  #  undef HEDLEY_REQUIRE
  #endif
  #define HEDLEY_REQUIRE(expr) HEDLEY_REQUIRE_MSG(expr, #expr)
  
  #if defined(HEDLEY_FLAGS)
  #  undef HEDLEY_FLAGS
  #endif
  #if HEDLEY_HAS_ATTRIBUTE(flag_enum)
  #  define HEDLEY_FLAGS __attribute__((__flag_enum__))
  #endif
  
  #if defined(HEDLEY_FLAGS_CAST)
  #  undef HEDLEY_FLAGS_CAST
  #endif
  #if HEDLEY_INTEL_VERSION_CHECK(19,0,0)
  #  define HEDLEY_FLAGS_CAST(T, expr) (__extension__ ({ \
    HEDLEY_DIAGNOSTIC_PUSH \
        _Pragma("warning(disable:188)") \
        ((T) (expr)); \
        HEDLEY_DIAGNOSTIC_POP \
      }))
  #else
  #  define HEDLEY_FLAGS_CAST(T, expr) HEDLEY_STATIC_CAST(T, expr)
  #endif
  
  /* Remaining macros are deprecated. */
  
  #if defined(HEDLEY_GCC_NOT_CLANG_VERSION_CHECK)
  #  undef HEDLEY_GCC_NOT_CLANG_VERSION_CHECK
  #endif
  #if defined(__clang__)
  #  define HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) (0)
  #else
  #  define HEDLEY_GCC_NOT_CLANG_VERSION_CHECK(major,minor,patch) HEDLEY_GCC_VERSION_CHECK(major,minor,patch)
  #endif
  
  #if defined(HEDLEY_CLANG_HAS_ATTRIBUTE)
  #  undef HEDLEY_CLANG_HAS_ATTRIBUTE
  #endif
  #define HEDLEY_CLANG_HAS_ATTRIBUTE(attribute) HEDLEY_HAS_ATTRIBUTE(attribute)
  
  #if defined(HEDLEY_CLANG_HAS_CPP_ATTRIBUTE)
  #  undef HEDLEY_CLANG_HAS_CPP_ATTRIBUTE
  #endif
  #define HEDLEY_CLANG_HAS_CPP_ATTRIBUTE(attribute) HEDLEY_HAS_CPP_ATTRIBUTE(attribute)
  
  #if defined(HEDLEY_CLANG_HAS_BUILTIN)
  #  undef HEDLEY_CLANG_HAS_BUILTIN
  #endif
  #define HEDLEY_CLANG_HAS_BUILTIN(builtin) HEDLEY_HAS_BUILTIN(builtin)
  
  #if defined(HEDLEY_CLANG_HAS_FEATURE)
  #  undef HEDLEY_CLANG_HAS_FEATURE
  #endif
  #define HEDLEY_CLANG_HAS_FEATURE(feature) HEDLEY_HAS_FEATURE(feature)
  
  #if defined(HEDLEY_CLANG_HAS_EXTENSION)
  #  undef HEDLEY_CLANG_HAS_EXTENSION
  #endif
  #define HEDLEY_CLANG_HAS_EXTENSION(extension) HEDLEY_HAS_EXTENSION(extension)
  
  #if defined(HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE)
  #  undef HEDLEY_CLANG_HAS_DECLSPEC_DECLSPEC_ATTRIBUTE
  #endif
  #define HEDLEY_CLANG_HAS_DECLSPEC_ATTRIBUTE(attribute) HEDLEY_HAS_DECLSPEC_ATTRIBUTE(attribute)
  
  #if defined(HEDLEY_CLANG_HAS_WARNING)
  #  undef HEDLEY_CLANG_HAS_WARNING
  #endif
  #define HEDLEY_CLANG_HAS_WARNING(warning) HEDLEY_HAS_WARNING(warning)
  
  #endif /* !defined(HEDLEY_VERSION) || (HEDLEY_VERSION < X) */
  
  
  namespace csv {
  #ifdef _MSC_VER
  #pragma region Compatibility Macros
  #endif
      /**
       *  @def IF_CONSTEXPR
       *  Expands to `if constexpr` in C++17 and `if` otherwise
       *
       *  @def CONSTEXPR_VALUE
       *  Expands to `constexpr` in C++17 and `const` otherwise.
       *  Mainly used for global variables.
       *
       *  @def CONSTEXPR
       *  Expands to `constexpr` in decent compilers and `inline` otherwise.
       *  Intended for functions and methods.
       */
  
  #if CMAKE_CXX_STANDARD == 17 || __cplusplus >= 201703L
  #define CSV_HAS_CXX17
  #endif
  
  #if CMAKE_CXX_STANDARD >= 14 || __cplusplus >= 	201402L
  #define CSV_HAS_CXX14
  #endif
  
  #ifdef CSV_HAS_CXX17
  #include <string_view>
       /** @typedef string_view
        *  The string_view class used by this library.
        */
      using string_view = std::string_view;
  #else
       /** @typedef string_view
        *  The string_view class used by this library.
        */
      using string_view = nonstd::string_view;
  #endif
  
  #ifdef CSV_HAS_CXX17
  #define IF_CONSTEXPR if constexpr
  #define CONSTEXPR_VALUE constexpr
  #else
  #define IF_CONSTEXPR if
  #define CONSTEXPR_VALUE const
  #endif
  
  #ifdef CSV_HAS_CXX14
      template<bool B, class T = void>
      using enable_if_t = std::enable_if_t<B, T>;
  #else
      template<bool B, class T = void>
      using enable_if_t = typename std::enable_if<B, T>::type;
  #endif
  
      // Resolves g++ bug with regard to constexpr methods
      // See: https://stackoverflow.com/questions/36489369/constexpr-non-static-member-function-with-non-constexpr-constructor-gcc-clang-d
  #if defined __GNUC__ && !defined __clang__
  #if (__GNUC__ >= 7 &&__GNUC_MINOR__ >= 2) || (__GNUC__ >= 8)
  #define CONSTEXPR constexpr
  #endif
  #else
  #ifdef CSV_HAS_CXX17
  #define CONSTEXPR constexpr
  #endif
  #endif
  
  #ifndef CONSTEXPR
  #define CONSTEXPR inline
  #endif
  
  #ifdef _MSC_VER
  #pragma endregion
  #endif
  
      namespace internals {
          // PAGE_SIZE macro could be already defined by the host system.
  #if defined(PAGE_SIZE)
  #undef PAGE_SIZE
  #endif
  
  // Get operating system specific details
  #if defined(_WIN32)
          inline int getpagesize() {
              _SYSTEM_INFO sys_info = {};
              GetSystemInfo(&sys_info);
              return std::max(sys_info.dwPageSize, sys_info.dwAllocationGranularity);
          }
  
          const int PAGE_SIZE = getpagesize();
  #elif defined(__linux__) 
          const int PAGE_SIZE = getpagesize();
  #else
          /** Size of a memory page in bytes. Used by
           *  csv::internals::CSVFieldArray when allocating blocks.
           */
          const int PAGE_SIZE = 4096;
  #endif
  
          /** For functions that lazy load a large CSV, this determines how
           *  many bytes are read at a time
           */
          constexpr size_t ITERATION_CHUNK_SIZE = 10000000; // 10MB
  
          template<typename T>
          inline bool is_equal(T a, T b, T epsilon = 0.001) {
              /** Returns true if two floating point values are about the same */
              static_assert(std::is_floating_point<T>::value, "T must be a floating point type.");
              return std::abs(a - b) < epsilon;
          }
  
          /**  @typedef ParseFlags
           *   An enum used for describing the significance of each character
           *   with respect to CSV parsing
           *
           *   @see quote_escape_flag
           */
          enum class ParseFlags {
              QUOTE_ESCAPE_QUOTE = 0, /**< A quote inside or terminating a quote_escaped field */
              QUOTE = 2 | 1,          /**< Characters which may signify a quote escape */
              NOT_SPECIAL = 4,        /**< Characters with no special meaning or escaped delimiters and newlines */
              DELIMITER = 4 | 2,      /**< Characters which signify a new field */
              NEWLINE = 4 | 2 | 1     /**< Characters which signify a new row */
          };
  
          /** Transform the ParseFlags given the context of whether or not the current
           *  field is quote escaped */
          constexpr ParseFlags quote_escape_flag(ParseFlags flag, bool quote_escape) noexcept {
              return (ParseFlags)((int)flag & ~((int)ParseFlags::QUOTE * quote_escape));
          }
  
          // Assumed to be true by parsing functions: allows for testing
          // if an item is DELIMITER or NEWLINE with a >= statement
          static_assert(ParseFlags::DELIMITER < ParseFlags::NEWLINE);
  
          /** Optimizations for reducing branching in parsing loop
           *
           *  Idea: The meaning of all non-quote characters changes depending
           *  on whether or not the parser is in a quote-escaped mode (0 or 1)
           */
          static_assert(quote_escape_flag(ParseFlags::NOT_SPECIAL, false) == ParseFlags::NOT_SPECIAL);
          static_assert(quote_escape_flag(ParseFlags::QUOTE, false) == ParseFlags::QUOTE);
          static_assert(quote_escape_flag(ParseFlags::DELIMITER, false) == ParseFlags::DELIMITER);
          static_assert(quote_escape_flag(ParseFlags::NEWLINE, false) == ParseFlags::NEWLINE);
  
          static_assert(quote_escape_flag(ParseFlags::NOT_SPECIAL, true) == ParseFlags::NOT_SPECIAL);
          static_assert(quote_escape_flag(ParseFlags::QUOTE, true) == ParseFlags::QUOTE_ESCAPE_QUOTE);
          static_assert(quote_escape_flag(ParseFlags::DELIMITER, true) == ParseFlags::NOT_SPECIAL);
          static_assert(quote_escape_flag(ParseFlags::NEWLINE, true) == ParseFlags::NOT_SPECIAL);
  
          /** An array which maps ASCII chars to a parsing flag */
          using ParseFlagMap = std::array<ParseFlags, 256>;
  
          /** An array which maps ASCII chars to a flag indicating if it is whitespace */
          using WhitespaceMap = std::array<bool, 256>;
      }
  
      /** Integer indicating a requested column wasn't found. */
      constexpr int CSV_NOT_FOUND = -1;
  }
  
  
  namespace csv {
      namespace internals {
          struct ColNames;
          using ColNamesPtr = std::shared_ptr<ColNames>;
  
          /** @struct ColNames
               *  A data structure for handling column name information.
               *
               *  These are created by CSVReader and passed (via smart pointer)
               *  to CSVRow objects it creates, thus
               *  allowing for indexing by column name.
               */
          struct ColNames {
          public:
              ColNames() = default;
              ColNames(const std::vector<std::string>& names) {
                  set_col_names(names);
              }
  
              std::vector<std::string> get_col_names() const;
              void set_col_names(const std::vector<std::string>&);
              int index_of(csv::string_view) const;
  
              bool empty() const noexcept { return this->col_names.empty(); }
              size_t size() const noexcept;
  
          private:
              std::vector<std::string> col_names;
              std::unordered_map<std::string, size_t> col_pos;
          };
      }
  }
  /** @file
   *  Defines an object used to store CSV format settings
   */
  
  #include <iterator>
  #include <stdexcept>
  #include <string>
  #include <vector>
  
  
  namespace csv {
      namespace internals {
          class IBasicCSVParser;
      }
  
      class CSVReader;
  
      /** Determines how to handle rows that are shorter or longer than the majority */
      enum class VariableColumnPolicy {
          THROW = -1,
          IGNORE_ROW = 0,
          KEEP   = 1
      };
  
      /** Stores the inferred format of a CSV file. */
      struct CSVGuessResult {
          char delim;
          int header_row;
      };
  
      /** Stores information about how to parse a CSV file.
       *  Can be used to construct a csv::CSVReader. 
       */
      class CSVFormat {
      public:
          /** Settings for parsing a RFC 4180 CSV file */
          CSVFormat() = default;
  
          /** Sets the delimiter of the CSV file
           *
           *  @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap
           */
          CSVFormat& delimiter(char delim);
  
          /** Sets a list of potential delimiters
           *  
           *  @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap
           *  @param[in] delim An array of possible delimiters to try parsing the CSV with
           */
          CSVFormat& delimiter(const std::vector<char> & delim);
  
          /** Sets the whitespace characters to be trimmed
           *
           *  @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap
           *  @param[in] ws An array of whitespace characters that should be trimmed
           */
          CSVFormat& trim(const std::vector<char> & ws);
  
          /** Sets the quote character
           *
           *  @throws `std::runtime_error` thrown if trim, quote, or possible delimiting characters overlap
           */
          CSVFormat& quote(char quote);
  
          /** Sets the column names.
           *
           *  @note Unsets any values set by header_row()
           */
          CSVFormat& column_names(const std::vector<std::string>& names);
  
          /** Sets the header row
           *
           *  @note Unsets any values set by column_names()
           */
          CSVFormat& header_row(int row);
  
          /** Tells the parser that this CSV has no header row
           *
           *  @note Equivalent to `header_row(-1)`
           *
           */
          CSVFormat& no_header() {
              this->header_row(-1);
              return *this;
          }
  
          /** Turn quoting on or off */
          CSVFormat& quote(bool use_quote) {
              this->no_quote = !use_quote;
              return *this;
          }
  
          /** Tells the parser how to handle columns of a different length than the others */
          CONSTEXPR CSVFormat& variable_columns(VariableColumnPolicy policy = VariableColumnPolicy::IGNORE_ROW) {
              this->variable_column_policy = policy;
              return *this;
          }
  
          /** Tells the parser how to handle columns of a different length than the others */
          CONSTEXPR CSVFormat& variable_columns(bool policy) {
              this->variable_column_policy = (VariableColumnPolicy)policy;
              return *this;
          }
  
          #ifndef DOXYGEN_SHOULD_SKIP_THIS
          char get_delim() const {
              // This error should never be received by end users.
              if (this->possible_delimiters.size() > 1) {
                  throw std::runtime_error("There is more than one possible delimiter.");
              }
  
              return this->possible_delimiters.at(0);
          }
  
          CONSTEXPR bool is_quoting_enabled() const { return !this->no_quote; }
          CONSTEXPR char get_quote_char() const { return this->quote_char; }
          CONSTEXPR int get_header() const { return this->header; }
          std::vector<char> get_possible_delims() const { return this->possible_delimiters; }
          std::vector<char> get_trim_chars() const { return this->trim_chars; }
          CONSTEXPR VariableColumnPolicy get_variable_column_policy() const { return this->variable_column_policy; }
          #endif
          
          /** CSVFormat for guessing the delimiter */
          CSV_INLINE static CSVFormat guess_csv() {
              CSVFormat format;
              format.delimiter({ ',', '|', '\t', ';', '^' })
                  .quote('"')
                  .header_row(0);
  
              return format;
          }
  
          bool guess_delim() {
              return this->possible_delimiters.size() > 1;
          }
  
          friend CSVReader;
          friend internals::IBasicCSVParser;
          
      private:
          /**< Throws an error if delimiters and trim characters overlap */
          void assert_no_char_overlap();
  
          /**< Set of possible delimiters */
          std::vector<char> possible_delimiters = { ',' };
  
          /**< Set of whitespace characters to trim */
          std::vector<char> trim_chars = {};
  
          /**< Row number with columns (ignored if col_names is non-empty) */
          int header = 0;
  
          /**< Whether or not to use quoting */
          bool no_quote = false;
  
          /**< Quote character */
          char quote_char = '"';
  
          /**< Should be left empty unless file doesn't include header */
          std::vector<std::string> col_names = {};
  
          /**< Allow variable length columns? */
          VariableColumnPolicy variable_column_policy = VariableColumnPolicy::IGNORE_ROW;
      };
  }
  /** @file
   *  Defines the data type used for storing information about a CSV row
   */
  
  #include <cmath>
  #include <iterator>
  #include <memory> // For CSVField
  #include <limits> // For CSVField
  #include <unordered_map>
  #include <unordered_set>
  #include <string>
  #include <sstream>
  #include <vector>
  
  /** @file
   *  @brief Implements data type parsing functionality
   */
  
  #include <cmath>
  #include <cctype>
  #include <string>
  #include <cassert>
  
  
  namespace csv {
      /** Enumerates the different CSV field types that are
       *  recognized by this library
       *
       *  @note Overflowing integers will be stored and classified as doubles.
       *  @note Unlike previous releases, integer enums here are platform agnostic.
       */
      enum class DataType {
          UNKNOWN = -1,
          CSV_NULL,   /**< Empty string */
          CSV_STRING, /**< Non-numeric string */
          CSV_INT8,   /**< 8-bit integer */
          CSV_INT16,  /**< 16-bit integer (short on MSVC/GCC) */
          CSV_INT32,  /**< 32-bit integer (int on MSVC/GCC) */
          CSV_INT64,  /**< 64-bit integer (long long on MSVC/GCC) */
          CSV_DOUBLE  /**< Floating point value */
      };
  
      static_assert(DataType::CSV_STRING < DataType::CSV_INT8, "String type should come before numeric types.");
      static_assert(DataType::CSV_INT8 < DataType::CSV_INT64, "Smaller integer types should come before larger integer types.");
      static_assert(DataType::CSV_INT64 < DataType::CSV_DOUBLE, "Integer types should come before floating point value types.");
  
      namespace internals {
          /** Compute 10 to the power of n */
          template<typename T>
          HEDLEY_CONST CONSTEXPR
          long double pow10(const T& n) noexcept {
              long double multiplicand = n > 0 ? 10 : 0.1,
                  ret = 1;
  
              // Make all numbers positive
              T iterations = n > 0 ? n : -n;
              
              for (T i = 0; i < iterations; i++) {
                  ret *= multiplicand;
              }
  
              return ret;
          }
  
          /** Compute 10 to the power of n */
          template<>
          HEDLEY_CONST CONSTEXPR
          long double pow10(const unsigned& n) noexcept {
              long double multiplicand = n > 0 ? 10 : 0.1,
                  ret = 1;
  
              for (unsigned i = 0; i < n; i++) {
                  ret *= multiplicand;
              }
  
              return ret;
          }
  
  #ifndef DOXYGEN_SHOULD_SKIP_THIS
          /** Private site-indexed array mapping byte sizes to an integer size enum */
          constexpr DataType int_type_arr[8] = {
              DataType::CSV_INT8,  // 1
              DataType::CSV_INT16, // 2
              DataType::UNKNOWN,
              DataType::CSV_INT32, // 4
              DataType::UNKNOWN,
              DataType::UNKNOWN,
              DataType::UNKNOWN,
              DataType::CSV_INT64  // 8
          };
  
          template<typename T>
          inline DataType type_num() {
              static_assert(std::is_integral<T>::value, "T should be an integral type.");
              static_assert(sizeof(T) <= 8, "Byte size must be no greater than 8.");
              return int_type_arr[sizeof(T) - 1];
          }
  
          template<> inline DataType type_num<float>() { return DataType::CSV_DOUBLE; }
          template<> inline DataType type_num<double>() { return DataType::CSV_DOUBLE; }
          template<> inline DataType type_num<long double>() { return DataType::CSV_DOUBLE; }
          template<> inline DataType type_num<std::nullptr_t>() { return DataType::CSV_NULL; }
          template<> inline DataType type_num<std::string>() { return DataType::CSV_STRING; }
  
          CONSTEXPR DataType data_type(csv::string_view in, long double* const out = nullptr);
  #endif
  
          /** Given a byte size, return the largest number than can be stored in
           *  an integer of that size
           *
           *  Note: Provides a platform-agnostic way of mapping names like "long int" to
           *  byte sizes
           */
          template<size_t Bytes>
          CONSTEXPR long double get_int_max() {
              static_assert(Bytes == 1 || Bytes == 2 || Bytes == 4 || Bytes == 8,
                  "Bytes must be a power of 2 below 8.");
  
              IF_CONSTEXPR (sizeof(signed char) == Bytes) {
                  return (long double)std::numeric_limits<signed char>::max();
              }
  
              IF_CONSTEXPR (sizeof(short) == Bytes) {
                  return (long double)std::numeric_limits<short>::max();
              }
  
              IF_CONSTEXPR (sizeof(int) == Bytes) {
                  return (long double)std::numeric_limits<int>::max();
              }
  
              IF_CONSTEXPR (sizeof(long int) == Bytes) {
                  return (long double)std::numeric_limits<long int>::max();
              }
  
              IF_CONSTEXPR (sizeof(long long int) == Bytes) {
                  return (long double)std::numeric_limits<long long int>::max();
              }
  
              HEDLEY_UNREACHABLE();
          }
  
          /** Given a byte size, return the largest number than can be stored in
           *  an unsigned integer of that size
           */
          template<size_t Bytes>
          CONSTEXPR long double get_uint_max() {
              static_assert(Bytes == 1 || Bytes == 2 || Bytes == 4 || Bytes == 8,
                  "Bytes must be a power of 2 below 8.");
  
              IF_CONSTEXPR(sizeof(unsigned char) == Bytes) {
                  return (long double)std::numeric_limits<unsigned char>::max();
              }
  
              IF_CONSTEXPR(sizeof(unsigned short) == Bytes) {
                  return (long double)std::numeric_limits<unsigned short>::max();
              }
  
              IF_CONSTEXPR(sizeof(unsigned int) == Bytes) {
                  return (long double)std::numeric_limits<unsigned int>::max();
              }
  
              IF_CONSTEXPR(sizeof(unsigned long int) == Bytes) {
                  return (long double)std::numeric_limits<unsigned long int>::max();
              }
  
              IF_CONSTEXPR(sizeof(unsigned long long int) == Bytes) {
                  return (long double)std::numeric_limits<unsigned long long int>::max();
              }
  
              HEDLEY_UNREACHABLE();
          }
  
          /** Largest number that can be stored in a 8-bit integer */
          CONSTEXPR_VALUE long double CSV_INT8_MAX = get_int_max<1>();
  
          /** Largest number that can be stored in a 16-bit integer */
          CONSTEXPR_VALUE long double CSV_INT16_MAX = get_int_max<2>();
  
          /** Largest number that can be stored in a 32-bit integer */
          CONSTEXPR_VALUE long double CSV_INT32_MAX = get_int_max<4>();
  
          /** Largest number that can be stored in a 64-bit integer */
          CONSTEXPR_VALUE long double CSV_INT64_MAX = get_int_max<8>();
  
          /** Largest number that can be stored in a 8-bit ungisned integer */
          CONSTEXPR_VALUE long double CSV_UINT8_MAX = get_uint_max<1>();
  
          /** Largest number that can be stored in a 16-bit unsigned integer */
          CONSTEXPR_VALUE long double CSV_UINT16_MAX = get_uint_max<2>();
  
          /** Largest number that can be stored in a 32-bit unsigned integer */
          CONSTEXPR_VALUE long double CSV_UINT32_MAX = get_uint_max<4>();
  
          /** Largest number that can be stored in a 64-bit unsigned integer */
          CONSTEXPR_VALUE long double CSV_UINT64_MAX = get_uint_max<8>();
  
          /** Given a pointer to the start of what is start of
           *  the exponential part of a number written (possibly) in scientific notation
           *  parse the exponent
           */
          HEDLEY_PRIVATE CONSTEXPR
          DataType _process_potential_exponential(
              csv::string_view exponential_part,
              const long double& coeff,
              long double * const out) {
              long double exponent = 0;
              auto result = data_type(exponential_part, &exponent);
  
              // Exponents in scientific notation should not be decimal numbers
              if (result >= DataType::CSV_INT8 && result < DataType::CSV_DOUBLE) {
                  if (out) *out = coeff * pow10(exponent);
                  return DataType::CSV_DOUBLE;
              }
  
              return DataType::CSV_STRING;
          }
  
          /** Given the absolute value of an integer, determine what numeric type
           *  it fits in
           */
          HEDLEY_PRIVATE HEDLEY_PURE CONSTEXPR
          DataType _determine_integral_type(const long double& number) noexcept {
              // We can assume number is always non-negative
              assert(number >= 0);
  
              if (number <= internals::CSV_INT8_MAX)
                  return DataType::CSV_INT8;
              else if (number <= internals::CSV_INT16_MAX)
                  return DataType::CSV_INT16;
              else if (number <= internals::CSV_INT32_MAX)
                  return DataType::CSV_INT32;
              else if (number <= internals::CSV_INT64_MAX)
                  return DataType::CSV_INT64;
              else // Conversion to long long will cause an overflow
                  return DataType::CSV_DOUBLE;
          }
  
          /** Distinguishes numeric from other text values. Used by various
           *  type casting functions, like csv_parser::CSVReader::read_row()
           *
           *  #### Rules
           *   - Leading and trailing whitespace ("padding") ignored
           *   - A string of just whitespace is NULL
           *
           *  @param[in]  in  String value to be examined
           *  @param[out] out Pointer to long double where results of numeric parsing
           *                  get stored
           */
          CONSTEXPR
          DataType data_type(csv::string_view in, long double* const out) {
              // Empty string --> NULL
              if (in.size() == 0)
                  return DataType::CSV_NULL;
  
              bool ws_allowed = true,
                  neg_allowed = true,
                  dot_allowed = true,
                  digit_allowed = true,
                  has_digit = false,
                  prob_float = false;
  
              unsigned places_after_decimal = 0;
              long double integral_part = 0,
                  decimal_part = 0;
  
              for (size_t i = 0, ilen = in.size(); i < ilen; i++) {
                  const char& current = in[i];
  
                  switch (current) {
                  case ' ':
                      if (!ws_allowed) {
                          if (isdigit(in[i - 1])) {
                              digit_allowed = false;
                              ws_allowed = true;
                          }
                          else {
                              // Ex: '510 123 4567'
                              return DataType::CSV_STRING;
                          }
                      }
                      break;
                  case '-':
                      if (!neg_allowed) {
                          // Ex: '510-123-4567'
                          return DataType::CSV_STRING;
                      }
  
                      neg_allowed = false;
                      break;
                  case '.':
                      if (!dot_allowed) {
                          return DataType::CSV_STRING;
                      }
  
                      dot_allowed = false;
                      prob_float = true;
                      break;
                  case 'e':
                  case 'E':
                      // Process scientific notation
                      if (prob_float || (i && i + 1 < ilen && isdigit(in[i - 1]))) {
                          size_t exponent_start_idx = i + 1;
                          prob_float = true;
  
                          // Strip out plus sign
                          if (in[i + 1] == '+') {
                              exponent_start_idx++;
                          }
  
                          return _process_potential_exponential(
                              in.substr(exponent_start_idx),
                              neg_allowed ? integral_part + decimal_part : -(integral_part + decimal_part),
                              out
                          );
                      }
  
                      return DataType::CSV_STRING;
                      break;
                  default:
                      short digit = current - '0';
                      if (digit >= 0 && digit <= 9) {
                          // Process digit
                          has_digit = true;
  
                          if (!digit_allowed)
                              return DataType::CSV_STRING;
                          else if (ws_allowed) // Ex: '510 456'
                              ws_allowed = false;
  
                          // Build current number
                          if (prob_float)
                              decimal_part += digit / pow10(++places_after_decimal);
                          else
                              integral_part = (integral_part * 10) + digit;
                      }
                      else {
                          return DataType::CSV_STRING;
                      }
                  }
              }
  
              // No non-numeric/non-whitespace characters found
              if (has_digit) {
                  long double number = integral_part + decimal_part;
                  if (out) {
                      *out = neg_allowed ? number : -number;
                  }
  
                  return prob_float ? DataType::CSV_DOUBLE : _determine_integral_type(number);
              }
  
              // Just whitespace
              return DataType::CSV_NULL;
          }
      }
  }
  
  namespace csv {
      namespace internals {
          class IBasicCSVParser;
  
          static const std::string ERROR_NAN = "Not a number.";
          static const std::string ERROR_OVERFLOW = "Overflow error.";
          static const std::string ERROR_FLOAT_TO_INT =
              "Attempted to convert a floating point value to an integral type.";
          static const std::string ERROR_NEG_TO_UNSIGNED = "Negative numbers cannot be converted to unsigned types.";
      
          std::string json_escape_string(csv::string_view s) noexcept;
  
          /** A barebones class used for describing CSV fields */
          struct RawCSVField {
              RawCSVField() = default;
              RawCSVField(size_t _start, size_t _length, bool _double_quote = false) {
                  start = _start;
                  length = _length;
                  has_double_quote = _double_quote;
              }
  
              /** The start of the field, relative to the beginning of the row */
              size_t start;
  
              /** The length of the row, ignoring quote escape characters */
              size_t length; 
  
              /** Whether or not the field contains an escaped quote */
              bool has_double_quote;
          };
  
          /** A class used for efficiently storing RawCSVField objects and expanding as necessary
           *
           *  @par Implementation
           *  This data structure stores RawCSVField in continguous blocks. When more capacity
           *  is needed, a new block is allocated, but previous data stays put.
           *
           *  @par Thread Safety
           *  This class may be safely read from multiple threads and written to from one,
           *  as long as the writing thread does not actively touch fields which are being
           *  read.
           */
          class CSVFieldList {
          public:
              /** Construct a CSVFieldList which allocates blocks of a certain size */
              CSVFieldList(size_t single_buffer_capacity = (size_t)(internals::PAGE_SIZE / sizeof(RawCSVField))) :
                  _single_buffer_capacity(single_buffer_capacity) {
                  this->allocate();
              }
  
              // No copy constructor
              CSVFieldList(const CSVFieldList& other) = delete;
  
              // CSVFieldArrays may be moved
              CSVFieldList(CSVFieldList&& other) :
                  _single_buffer_capacity(other._single_buffer_capacity) {
                  buffers = std::move(other.buffers);
                  _current_buffer_size = other._current_buffer_size;
                  _back = other._back;
              }
  
              ~CSVFieldList() {
                  for (auto& buffer : buffers)
                      delete[] buffer;
              }
  
              template <class... Args>
              void emplace_back(Args&&... args) {
                  if (this->_current_buffer_size == this->_single_buffer_capacity) {
                      this->allocate();
                  }
  
                  *(_back++) = RawCSVField(std::forward<Args>(args)...);
                  _current_buffer_size++;
              }
  
              size_t size() const noexcept {
                  return this->_current_buffer_size + ((this->buffers.size() - 1) * this->_single_buffer_capacity);
              }
  
              RawCSVField& operator[](size_t n) const;
  
          private:
              const size_t _single_buffer_capacity;
  
              std::vector<RawCSVField*> buffers = {};
  
              /** Number of items in the current buffer */
              size_t _current_buffer_size = 0;
  
              /** Pointer to the current empty field */
              RawCSVField* _back = nullptr;
  
              /** Allocate a new page of memory */
              void allocate();
          };
  
  
          /** A class for storing raw CSV data and associated metadata */
          struct RawCSVData {
              std::shared_ptr<void> _data = nullptr;
              csv::string_view data = "";
  
              internals::CSVFieldList fields;
  
              std::unordered_set<size_t> has_double_quotes = {};
  
              // TODO: Consider replacing with a more thread-safe structure
              std::unordered_map<size_t, std::string> double_quote_fields = {};
  
              internals::ColNamesPtr col_names = nullptr;
              internals::ParseFlagMap parse_flags;
              internals::WhitespaceMap ws_flags;
          };
  
          using RawCSVDataPtr = std::shared_ptr<RawCSVData>;
      }
  
      /**
      * @class CSVField
      * @brief Data type representing individual CSV values.
      *        CSVFields can be obtained by using CSVRow::operator[]
      */
      class CSVField {
      public:
          /** Constructs a CSVField from a string_view */
          constexpr explicit CSVField(csv::string_view _sv) noexcept : sv(_sv) { };
  
          operator std::string() const {
              return std::string("<CSVField> ") + std::string(this->sv);
          }
  
          /** Returns the value casted to the requested type, performing type checking before.
          *
          *  \par Valid options for T
          *   - std::string or csv::string_view
          *   - signed integral types (signed char, short, int, long int, long long int)
          *   - floating point types (float, double, long double)
          *   - unsigned integers are not supported at this time, but may be in a later release
          *
          *  \par Invalid conversions
          *   - Converting non-numeric values to any numeric type
          *   - Converting floating point values to integers
          *   - Converting a large integer to a smaller type that will not hold it
          *
          *  @note    This method is capable of parsing scientific E-notation.
          *           See [this page](md_docs_source_scientific_notation.html)
          *           for more details.
          *
          *  @throws  std::runtime_error Thrown if an invalid conversion is performed.
          *
          *  @warning Currently, conversions to floating point types are not
          *           checked for loss of precision
          *
          *  @warning Any string_views returned are only guaranteed to be valid
          *           if the parent CSVRow is still alive. If you are concerned
          *           about object lifetimes, then grab a std::string or a
          *           numeric value.
          *
          */
          template<typename T = std::string> T get() {
              IF_CONSTEXPR(std::is_arithmetic<T>::value) {
                  // Note: this->type() also converts the CSV value to float
                  if (this->type() <= DataType::CSV_STRING) {
                      throw std::runtime_error(internals::ERROR_NAN);
                  }
              }
  
              IF_CONSTEXPR(std::is_integral<T>::value) {
                  // Note: this->is_float() also converts the CSV value to float
                  if (this->is_float()) {
                      throw std::runtime_error(internals::ERROR_FLOAT_TO_INT);
                  }
  
                  IF_CONSTEXPR(std::is_unsigned<T>::value) {
                      if (this->value < 0) {
                          throw std::runtime_error(internals::ERROR_NEG_TO_UNSIGNED);
                      }
                  }
              }
  
              // Allow fallthrough from previous if branch
              IF_CONSTEXPR(!std::is_floating_point<T>::value) {
                  IF_CONSTEXPR(std::is_unsigned<T>::value) {
                      // Quick hack to perform correct unsigned integer boundary checks
                      if (this->value > internals::get_uint_max<sizeof(T)>()) {
                          throw std::runtime_error(internals::ERROR_OVERFLOW);
                      }
                  }
                  else if (internals::type_num<T>() < this->_type) {
                      throw std::runtime_error(internals::ERROR_OVERFLOW);
                  }
              }
  
              return static_cast<T>(this->value);
          }
  
          /** Compares the contents of this field to a numeric value. If this
           *  field does not contain a numeric value, then all comparisons return
           *  false.
           *
           *  @note    Floating point values are considered equal if they are within
           *           `0.000001` of each other.
           *
           *  @warning Multiple numeric comparisons involving the same field can
           *           be done more efficiently by calling the CSVField::get<>() method.
           *
           *  @sa      csv::CSVField::operator==(const char * other)
           *  @sa      csv::CSVField::operator==(csv::string_view other)
           */
          template<typename T>
          CONSTEXPR bool operator==(T other) const noexcept
          {
              static_assert(std::is_arithmetic<T>::value,
                  "T should be a numeric value.");
  
              if (this->_type != DataType::UNKNOWN) {
                  if (this->_type == DataType::CSV_STRING) {
                      return false;
                  }
  
                  return internals::is_equal(value, static_cast<long double>(other), 0.000001L);
              }
  
              long double out = 0;
              if (internals::data_type(this->sv, &out) == DataType::CSV_STRING) {
                  return false;
              }
  
              return internals::is_equal(out, static_cast<long double>(other), 0.000001L);
          }
  
          /** Return a string view over the field's contents */
          CONSTEXPR csv::string_view get_sv() const noexcept { return this->sv; }
  
          /** Returns true if field is an empty string or string of whitespace characters */
          CONSTEXPR bool is_null() noexcept { return type() == DataType::CSV_NULL; }
  
          /** Returns true if field is a non-numeric, non-empty string */
          CONSTEXPR bool is_str() noexcept { return type() == DataType::CSV_STRING; }
  
          /** Returns true if field is an integer or float */
          CONSTEXPR bool is_num() noexcept { return type() >= DataType::CSV_INT8; }
  
          /** Returns true if field is an integer */
          CONSTEXPR bool is_int() noexcept {
              return (type() >= DataType::CSV_INT8) && (type() <= DataType::CSV_INT64);
          }
  
          /** Returns true if field is a floating point value */
          CONSTEXPR bool is_float() noexcept { return type() == DataType::CSV_DOUBLE; };
  
          /** Return the type of the underlying CSV data */
          CONSTEXPR DataType type() noexcept {
              this->get_value();
              return _type;
          }
  
      private:
          long double value = 0;    /**< Cached numeric value */
          csv::string_view sv = ""; /**< A pointer to this field's text */
          DataType _type = DataType::UNKNOWN; /**< Cached data type value */
          CONSTEXPR void get_value() noexcept {
              /* Check to see if value has been cached previously, if not
               * evaluate it
               */
              if ((int)_type < 0) {
                  this->_type = internals::data_type(this->sv, &this->value);
              }
          }
      };
  
      /** Data structure for representing CSV rows */
      class CSVRow {
      public:
          friend internals::IBasicCSVParser;
  
          CSVRow() = default;
          
          /** Construct a CSVRow from a RawCSVDataPtr */
          CSVRow(internals::RawCSVDataPtr _data) : data(_data) {}
          CSVRow(internals::RawCSVDataPtr _data, size_t _data_start, size_t _field_bounds)
              : data(_data), data_start(_data_start), fields_start(_field_bounds) {}
  
          /** Indicates whether row is empty or not */
          CONSTEXPR bool empty() const noexcept { return this->size() == 0; }
  
          /** Return the number of fields in this row */
          CONSTEXPR size_t size() const noexcept { return row_length; }
  
          /** @name Value Retrieval */
          ///@{
          CSVField operator[](size_t n) const;
          CSVField operator[](const std::string&) const;
          std::string to_json(const std::vector<std::string>& subset = {}) const;
          std::string to_json_array(const std::vector<std::string>& subset = {}) const;
  
          /** Retrieve this row's associated column names */
          std::vector<std::string> get_col_names() const {
              return this->data->col_names->get_col_names();
          }
  
          /** Convert this CSVRow into a vector of strings.
           *  **Note**: This is a less efficient method of
           *  accessing data than using the [] operator.
           */
          operator std::vector<std::string>() const;
          ///@}
  
          /** A random access iterator over the contents of a CSV row.
           *  Each iterator points to a CSVField.
           */
          class iterator {
          public:
  #ifndef DOXYGEN_SHOULD_SKIP_THIS
              using value_type = CSVField;
              using difference_type = int;
  
              // Using CSVField * as pointer type causes segfaults in MSVC debug builds
              // but using shared_ptr as pointer type won't compile in g++
  #ifdef _MSC_BUILD
              using pointer = std::shared_ptr<CSVField>;
  #else
              using pointer = CSVField * ;
  #endif
  
              using reference = CSVField & ;
              using iterator_category = std::random_access_iterator_tag;
  #endif
              iterator(const CSVRow*, int i);
  
              reference operator*() const;
              pointer operator->() const;
  
              iterator operator++(int);
              iterator& operator++();
              iterator operator--(int);
              iterator& operator--();
              iterator operator+(difference_type n) const;
              iterator operator-(difference_type n) const;
  
              /** Two iterators are equal if they point to the same field */
              CONSTEXPR bool operator==(const iterator& other) const noexcept {
                  return this->i == other.i;
              };
  
              CONSTEXPR bool operator!=(const iterator& other) const noexcept { return !operator==(other); }
  
  #ifndef NDEBUG
              friend CSVRow;
  #endif
  
          private:
              const CSVRow * daddy = nullptr;            // Pointer to parent
              std::shared_ptr<CSVField> field = nullptr; // Current field pointed at
              int i = 0;                                 // Index of current field
          };
  
          /** A reverse iterator over the contents of a CSVRow. */
          using reverse_iterator = std::reverse_iterator<iterator>;
  
          /** @name Iterators
           *  @brief Each iterator points to a CSVField object.
           */
           ///@{
          iterator begin() const;
          iterator end() const noexcept;
          reverse_iterator rbegin() const noexcept;
          reverse_iterator rend() const;
          ///@}
  
      private:
          /** Retrieve a string view corresponding to the specified index */
          csv::string_view get_field(size_t index) const;
  
          internals::RawCSVDataPtr data;
  
          /** Where in RawCSVData.data we start */
          size_t data_start = 0;
  
          /** Where in the RawCSVDataPtr.fields array we start */
          size_t fields_start = 0;
  
          /** How many columns this row spans */
          size_t row_length = 0;
      };
  
  #ifdef _MSC_VER
  #pragma region CSVField::get Specializations
  #endif
      /** Retrieve this field's original string */
      template<>
      inline std::string CSVField::get<std::string>() {
          return std::string(this->sv);
      }
  
      /** Retrieve a view over this field's string
       *
       *  @warning This string_view is only guaranteed to be valid as long as this
       *           CSVRow is still alive.
       */
      template<>
      CONSTEXPR csv::string_view CSVField::get<csv::string_view>() {
          return this->sv;
      }
  
      /** Retrieve this field's value as a long double */
      template<>
      CONSTEXPR long double CSVField::get<long double>() {
          if (!is_num())
              throw std::runtime_error(internals::ERROR_NAN);
  
          return this->value;
      }
  #ifdef _MSC_VER
  #pragma endregion CSVField::get Specializations
  #endif
  
      /** Compares the contents of this field to a string */
      template<>
      CONSTEXPR bool CSVField::operator==(const char * other) const noexcept
      {
          return this->sv == other;
      }
  
      /** Compares the contents of this field to a string */
      template<>
      CONSTEXPR bool CSVField::operator==(csv::string_view other) const noexcept
      {
          return this->sv == other;
      }
  }
  
  inline std::ostream& operator << (std::ostream& os, csv::CSVField const& value) {
      os << std::string(value);
      return os;
  }
  
  
  namespace csv {
      namespace internals {
          /** Create a vector v where each index i corresponds to the
           *  ASCII number for a character and, v[i + 128] labels it according to
           *  the CSVReader::ParseFlags enum
           */
          HEDLEY_CONST CONSTEXPR ParseFlagMap make_parse_flags(char delimiter) {
              std::array<ParseFlags, 256> ret = {};
              for (int i = -128; i < 128; i++) {
                  const int arr_idx = i + 128;
                  char ch = char(i);
  
                  if (ch == delimiter)
                      ret[arr_idx] = ParseFlags::DELIMITER;
                  else if (ch == '\r' || ch == '\n')
                      ret[arr_idx] = ParseFlags::NEWLINE;
                  else
                      ret[arr_idx] = ParseFlags::NOT_SPECIAL;
              }
  
              return ret;
          }
  
          /** Create a vector v where each index i corresponds to the
           *  ASCII number for a character and, v[i + 128] labels it according to
           *  the CSVReader::ParseFlags enum
           */
          HEDLEY_CONST CONSTEXPR ParseFlagMap make_parse_flags(char delimiter, char quote_char) {
              std::array<ParseFlags, 256> ret = make_parse_flags(delimiter);
              ret[(size_t)quote_char + 128] = ParseFlags::QUOTE;
              return ret;
          }
  
          /** Create a vector v where each index i corresponds to the
           *  ASCII number for a character c and, v[i + 128] is true if
           *  c is a whitespace character
           */
          HEDLEY_CONST CONSTEXPR WhitespaceMap make_ws_flags(const char* ws_chars, size_t n_chars) {
              std::array<bool, 256> ret = {};
              for (int i = -128; i < 128; i++) {
                  const int arr_idx = i + 128;
                  char ch = char(i);
                  ret[arr_idx] = false;
  
                  for (size_t j = 0; j < n_chars; j++) {
                      if (ws_chars[j] == ch) {
                          ret[arr_idx] = true;
                      }
                  }
              }
  
              return ret;
          }
  
          inline WhitespaceMap make_ws_flags(const std::vector<char>& flags) {
              return make_ws_flags(flags.data(), flags.size());
          }
  
          CSV_INLINE size_t get_file_size(csv::string_view filename);
  
          CSV_INLINE std::string get_csv_head(csv::string_view filename);
  
          /** Read the first 500KB of a CSV file */
          CSV_INLINE std::string get_csv_head(csv::string_view filename, size_t file_size);
  
          /** A std::deque wrapper which allows multiple read and write threads to concurrently
           *  access it along with providing read threads the ability to wait for the deque
           *  to become populated
           */
          template<typename T>
          class ThreadSafeDeque {
          public:
              ThreadSafeDeque(size_t notify_size = 100) : _notify_size(notify_size) {};
              ThreadSafeDeque(const ThreadSafeDeque& other) {
                  this->data = other.data;
                  this->_notify_size = other._notify_size;
              }
  
              ThreadSafeDeque(const std::deque<T>& source) : ThreadSafeDeque() {
                  this->data = source;
              }
  
              void clear() noexcept { this->data.clear(); }
  
              bool empty() const noexcept {
                  return this->data.empty();
              }
  
              T& front() noexcept {
                  return this->data.front();
              }
  
              T& operator[](size_t n) {
                  return this->data[n];
              }
  
              void push_back(T&& item) {
                  std::lock_guard<std::mutex> lock{ this->_lock };
                  this->data.push_back(std::move(item));
  
                  if (this->size() >= _notify_size) {
                      this->_cond.notify_all();
                  }
              }
  
              T pop_front() noexcept {
                  std::lock_guard<std::mutex> lock{ this->_lock };
                  T item = std::move(data.front());
                  data.pop_front();
                  return item;
              }
  
              size_t size() const noexcept { return this->data.size(); }
  
              /** Returns true if a thread is actively pushing items to this deque */
              constexpr bool is_waitable() const noexcept { return this->_is_waitable; }
  
              /** Wait for an item to become available */
              void wait() {
                  if (!is_waitable()) {
                      return;
                  }
  
                  std::unique_lock<std::mutex> lock{ this->_lock };
                  this->_cond.wait(lock, [this] { return this->size() >= _notify_size || !this->is_waitable(); });
                  lock.unlock();
              }
  
              typename std::deque<T>::iterator begin() noexcept {
                  return this->data.begin();
              }
  
              typename std::deque<T>::iterator end() noexcept {
                  return this->data.end();
              }
  
              /** Tell listeners that this deque is actively being pushed to */
              void notify_all() {
                  std::unique_lock<std::mutex> lock{ this->_lock };
                  this->_is_waitable = true;
                  this->_cond.notify_all();
              }
  
              /** Tell all listeners to stop */
              void kill_all() {
                  std::unique_lock<std::mutex> lock{ this->_lock };
                  this->_is_waitable = false;
                  this->_cond.notify_all();
              }
  
          private:
              bool _is_waitable = false;
              size_t _notify_size;
              std::mutex _lock;
              std::condition_variable _cond;
              std::deque<T> data;
          };
  
          constexpr const int UNINITIALIZED_FIELD = -1;
      }
  
      /** Standard type for storing collection of rows */
      using RowCollection = internals::ThreadSafeDeque<CSVRow>;
  
      namespace internals {
          /** Abstract base class which provides CSV parsing logic.
           *
           *  Concrete implementations may customize this logic across
           *  different input sources, such as memory mapped files, stringstreams,
           *  etc...
           */
          class IBasicCSVParser {
          public:
              IBasicCSVParser() = default;
              IBasicCSVParser(const CSVFormat&, const ColNamesPtr&);
              IBasicCSVParser(const ParseFlagMap& parse_flags, const WhitespaceMap& ws_flags
              ) : _parse_flags(parse_flags), _ws_flags(ws_flags) {};
  
              virtual ~IBasicCSVParser() {}
  
              /** Whether or not we have reached the end of source */
              bool eof() { return this->_eof; }
  
              /** Parse the next block of data */
              virtual void next(size_t bytes) = 0;
  
              /** Indicate the last block of data has been parsed */
              void end_feed();
  
              CONSTEXPR ParseFlags parse_flag(const char ch) const noexcept {
                  return _parse_flags.data()[ch + 128];
              }
  
              CONSTEXPR ParseFlags compound_parse_flag(const char ch) const noexcept {
                  return quote_escape_flag(parse_flag(ch), this->quote_escape);
              }
  
              /** Whether or not this CSV has a UTF-8 byte order mark */
              CONSTEXPR bool utf8_bom() const { return this->_utf8_bom; }
  
              void set_output(RowCollection& rows) { this->_records = &rows; }
  
          protected:
              /** @name Current Parser State */
              ///@{
              CSVRow current_row;
              RawCSVDataPtr data_ptr = nullptr;
              ColNamesPtr _col_names = nullptr;
              CSVFieldList* fields = nullptr;
              int field_start = UNINITIALIZED_FIELD;
              size_t field_length = 0;
  
              /** An array where the (i + 128)th slot gives the ParseFlags for ASCII character i */
              ParseFlagMap _parse_flags;
              ///@}
  
              /** @name Current Stream/File State */
              ///@{
              bool _eof = false;
  
              /** The size of the incoming CSV */
              size_t source_size = 0;
              ///@}
  
              /** Whether or not source needs to be read in chunks */
              CONSTEXPR bool no_chunk() const { return this->source_size < ITERATION_CHUNK_SIZE; }
  
              /** Parse the current chunk of data *
               *
               *  @returns How many character were read that are part of complete rows
               */
              size_t parse();
  
              /** Create a new RawCSVDataPtr for a new chunk of data */
              void reset_data_ptr();
          private:
              /** An array where the (i + 128)th slot determines whether ASCII character i should
               *  be trimmed
               */
              WhitespaceMap _ws_flags;
              bool quote_escape = false;
              bool field_has_double_quote = false;
  
              /** Where we are in the current data block */
              size_t data_pos = 0;
  
              /** Whether or not an attempt to find Unicode BOM has been made */
              bool unicode_bom_scan = false;
              bool _utf8_bom = false;
              
              /** Where complete rows should be pushed to */
              RowCollection* _records = nullptr;
  
              CONSTEXPR bool ws_flag(const char ch) const noexcept {
                  return _ws_flags.data()[ch + 128];
              }
  
              size_t& current_row_start() {
                  return this->current_row.data_start;
              }
      
              void parse_field() noexcept;
  
              /** Finish parsing the current field */
              void push_field();
  
              /** Finish parsing the current row */
              void push_row();
  
              /** Handle possible Unicode byte order mark */
              void trim_utf8_bom();
          };
  
          /** A class for parsing CSV data from a `std::stringstream`
           *  or an `std::ifstream`
           */
          template<typename TStream>
          class StreamParser: public IBasicCSVParser {
              using RowCollection = ThreadSafeDeque<CSVRow>;
  
          public:
              StreamParser(TStream& source,
                  const CSVFormat& format,
                  const ColNamesPtr& col_names = nullptr
              ) : _source(std::move(source)), IBasicCSVParser(format, col_names) {};
  
              StreamParser(
                  TStream& source,
                  internals::ParseFlagMap parse_flags,
                  internals::WhitespaceMap ws_flags) :
                  IBasicCSVParser(parse_flags, ws_flags),
                  _source(std::move(source))
              {};
  
              ~StreamParser() {}
  
              void next(size_t bytes = ITERATION_CHUNK_SIZE) override {
                  if (this->eof()) return;
  
                  this->reset_data_ptr();
                  this->data_ptr->_data = std::make_shared<std::string>();
  
                  if (source_size == 0) {
                      const auto start = _source.tellg();
                      _source.seekg(0, std::ios::end);
                      const auto end = _source.tellg();
                      _source.seekg(0, std::ios::beg);
  
                      source_size = end - start;
                  }
  
                  // Read data into buffer
                  size_t length = std::min(source_size - stream_pos, bytes);
                  std::unique_ptr<char[]> buff(new char[length]);
                  _source.seekg(stream_pos, std::ios::beg);
                  _source.read(buff.get(), length);
                  stream_pos = _source.tellg();
                  ((std::string*)(this->data_ptr->_data.get()))->assign(buff.get(), length);
  
                  // Create string_view
                  this->data_ptr->data = *((std::string*)this->data_ptr->_data.get());
  
                  // Parse
                  this->current_row = CSVRow(this->data_ptr);
                  size_t remainder = this->parse();
  
                  if (stream_pos == source_size || no_chunk()) {
                      this->_eof = true;
                      this->end_feed();
                  }
                  else {
                      this->stream_pos -= (length - remainder);
                  }
              }
  
          private:
              TStream _source;
              size_t stream_pos = 0;
          };
  
          /** Parser for memory-mapped files
           *
           *  @par Implementation
           *  This class constructs moving windows over a file to avoid
           *  creating massive memory maps which may require more RAM
           *  than the user has available. It contains logic to automatically
           *  re-align each memory map to the beginning of a CSV row.
           *
           */
          class MmapParser : public IBasicCSVParser {
          public:
              MmapParser(csv::string_view filename,
                  const CSVFormat& format,
                  const ColNamesPtr& col_names = nullptr
              ) : IBasicCSVParser(format, col_names) {
                  this->_filename = filename.data();
                  this->source_size = get_file_size(filename);
              };
  
              ~MmapParser() {}
  
              void next(size_t bytes) override;
  
          private:
              std::string _filename;
              size_t mmap_pos = 0;
          };
      }
  }
  
  /** The all encompassing namespace */
  namespace csv {
      /** Stuff that is generally not of interest to end-users */
      namespace internals {
          std::string format_row(const std::vector<std::string>& row, csv::string_view delim = ", ");
  
          std::vector<std::string> _get_col_names( csv::string_view head, const CSVFormat format = CSVFormat::guess_csv());
  
          struct GuessScore {
              double score;
              size_t header;
          };
  
          CSV_INLINE GuessScore calculate_score(csv::string_view head, CSVFormat format);
  
          CSVGuessResult _guess_format(csv::string_view head, const std::vector<char>& delims = { ',', '|', '\t', ';', '^', '~' });
      }
  
      std::vector<std::string> get_col_names(
          csv::string_view filename,
          const CSVFormat format = CSVFormat::guess_csv());
  
      /** Guess the delimiter used by a delimiter-separated values file */
      CSVGuessResult guess_format(csv::string_view filename,
          const std::vector<char>& delims = { ',', '|', '\t', ';', '^', '~' });
  
      /** @class CSVReader
       *  @brief Main class for parsing CSVs from files and in-memory sources
       *
       *  All rows are compared to the column names for length consistency
       *  - By default, rows that are too short or too long are dropped
       *  - Custom behavior can be defined by overriding bad_row_handler in a subclass
       */
      class CSVReader {
      public:
          /**
           * An input iterator capable of handling large files.
           * @note Created by CSVReader::begin() and CSVReader::end().
           *
           * @par Iterating over a file
           * @snippet tests/test_csv_iterator.cpp CSVReader Iterator 1
           *
           * @par Using with `<algorithm>` library
           * @snippet tests/test_csv_iterator.cpp CSVReader Iterator 2
           */
          class iterator {
          public:
              #ifndef DOXYGEN_SHOULD_SKIP_THIS
              using value_type = CSVRow;
              using difference_type = std::ptrdiff_t;
              using pointer = CSVRow * ;
              using reference = CSVRow & ;
              using iterator_category = std::input_iterator_tag;
              #endif
  
              iterator() = default;
              iterator(CSVReader* reader) : daddy(reader) {};
              iterator(CSVReader*, CSVRow&&);
  
              /** Access the CSVRow held by the iterator */
              CONSTEXPR reference operator*() { return this->row; }
  
              /** Return a pointer to the CSVRow the iterator has stopped at */
              CONSTEXPR pointer operator->() { return &(this->row); }
  
              iterator& operator++();   /**< Pre-increment iterator */
              iterator operator++(int); /**< Post-increment ierator */
              iterator& operator--();
  
              /** Returns true if iterators were constructed from the same CSVReader
               *  and point to the same row
               */
              CONSTEXPR bool operator==(const iterator& other) const noexcept {
                  return (this->daddy == other.daddy) && (this->i == other.i);
              }
  
              CONSTEXPR bool operator!=(const iterator& other) const noexcept { return !operator==(other); }
          private:
              CSVReader * daddy = nullptr;  // Pointer to parent
              CSVRow row;                   // Current row
              size_t i = 0;               // Index of current row
          };
  
          /** @name Constructors
           *  Constructors for iterating over large files and parsing in-memory sources.
           */
           ///@{
          CSVReader(csv::string_view filename, CSVFormat format = CSVFormat::guess_csv());
  
          /** Allows parsing stream sources such as `std::stringstream` or `std::ifstream`
           *
           *  @tparam TStream An input stream deriving from `std::istream`
           *  @note   Currently this constructor requires special CSV dialects to be manually
           *          specified.
           */
          template<typename TStream,
              csv::enable_if_t<std::is_base_of<std::istream, TStream>::value, int> = 0>
          CSVReader(TStream& source, CSVFormat format = CSVFormat()) : _format(format) {
              using Parser = internals::StreamParser<TStream>;
  
              if (!format.col_names.empty())
                  this->set_col_names(format.col_names);
  
              this->parser = std::unique_ptr<Parser>(
                  new Parser(source, format, col_names)); // For C++11
              this->initial_read();
          }
          ///@}
  
          CSVReader(const CSVReader&) = delete; // No copy constructor
          CSVReader(CSVReader&&) = default;     // Move constructor
          CSVReader& operator=(const CSVReader&) = delete; // No copy assignment
          CSVReader& operator=(CSVReader&& other) = default;
          ~CSVReader() {
              if (this->read_csv_worker.joinable()) {
                  this->read_csv_worker.join();
              }
          }
  
          /** @name Retrieving CSV Rows */
          ///@{
          bool read_row(CSVRow &row);
          iterator begin();
          HEDLEY_CONST iterator end() const noexcept;
  
          /** Returns true if we have reached end of file */
          bool eof() const noexcept { return this->parser->eof(); };
          ///@}
  
          /** @name CSV Metadata */
          ///@{
          CSVFormat get_format() const;
          std::vector<std::string> get_col_names() const;
          int index_of(csv::string_view col_name) const;
          ///@}
          
          /** @name CSV Metadata: Attributes */
          ///@{
          /** Whether or not the file or stream contains valid CSV rows,
           *  not including the header.
           *
           *  @note Gives an accurate answer regardless of when it is called.
           *
           */
          CONSTEXPR bool empty() const noexcept { return this->n_rows() == 0; }
  
          /** Retrieves the number of rows that have been read so far */
          CONSTEXPR size_t n_rows() const noexcept { return this->_n_rows; }
  
          /** Whether or not CSV was prefixed with a UTF-8 bom */
          bool utf8_bom() const noexcept { return this->parser->utf8_bom(); }
          ///@}
  
      protected:
          /**
           * \defgroup csv_internal CSV Parser Internals
           * @brief Internals of CSVReader. Only maintainers and those looking to
           *        extend the parser should read this.
           * @{
           */
  
          /** Sets this reader's column names and associated data */
          void set_col_names(const std::vector<std::string>&);
  
          /** @name CSV Settings **/
          ///@{
          CSVFormat _format;
          ///@}
  
          /** @name Parser State */
          ///@{
          /** Pointer to a object containing column information */
          internals::ColNamesPtr col_names = std::make_shared<internals::ColNames>();
  
          /** Helper class which actually does the parsing */
          std::unique_ptr<internals::IBasicCSVParser> parser = nullptr;
  
          /** Queue of parsed CSV rows */
          RowCollection records = RowCollection(100);
  
          size_t n_cols = 0;  /**< The number of columns in this CSV */
          size_t _n_rows = 0; /**< How many rows (minus header) have been read so far */
  
          /** @name Multi-Threaded File Reading Functions */
          ///@{
          bool read_csv(size_t bytes = internals::ITERATION_CHUNK_SIZE);
          ///@}
  
          /**@}*/
  
      private:
          /** Whether or not rows before header were trimmed */
          bool header_trimmed = false;
  
          /** @name Multi-Threaded File Reading: Flags and State */
          ///@{
          std::thread read_csv_worker; /**< Worker thread for read_csv() */
          ///@}
  
          /** Read initial chunk to get metadata */
          void initial_read() {
              this->read_csv_worker = std::thread(&CSVReader::read_csv, this, internals::ITERATION_CHUNK_SIZE);
              this->read_csv_worker.join();
          }
  
          void trim_header();
      };
  }
  /** @file
   *  Calculates statistics from CSV files
   */
  
  #include <unordered_map>
  #include <sstream>
  #include <vector>
  
  namespace csv {
      /** Class for calculating statistics from CSV files and in-memory sources
       *
       *  **Example**
       *  \include programs/csv_stats.cpp
       *
       */
      class CSVStat {
      public:
          using FreqCount = std::unordered_map<std::string, size_t>;
          using TypeCount = std::unordered_map<DataType, size_t>;
  
          std::vector<long double> get_mean() const;
          std::vector<long double> get_variance() const;
          std::vector<long double> get_mins() const;
          std::vector<long double> get_maxes() const;
          std::vector<FreqCount> get_counts() const;
          std::vector<TypeCount> get_dtypes() const;
  
          std::vector<std::string> get_col_names() const {
              return this->reader.get_col_names();
          }
  
          CSVStat(csv::string_view filename, CSVFormat format = CSVFormat::guess_csv());
          CSVStat(std::stringstream& source, CSVFormat format = CSVFormat());
      private:
          // An array of rolling averages
          // Each index corresponds to the rolling mean for the column at said index
          std::vector<long double> rolling_means;
          std::vector<long double> rolling_vars;
          std::vector<long double> mins;
          std::vector<long double> maxes;
          std::vector<FreqCount> counts;
          std::vector<TypeCount> dtypes;
          std::vector<long double> n;
  
          // Statistic calculators
          void variance(const long double&, const size_t&);
          void count(CSVField&, const size_t&);
          void min_max(const long double&, const size_t&);
          void dtype(CSVField&, const size_t&);
  
          void calc();
          void calc_worker(const size_t&);
  
          CSVReader reader;
          std::deque<CSVRow> records = {};
      };
  }
  
  #include <string>
  #include <type_traits>
  #include <unordered_map>
  
  namespace csv {
      /** Returned by get_file_info() */
      struct CSVFileInfo {
          std::string filename;               /**< Filename */
          std::vector<std::string> col_names; /**< CSV column names */
          char delim;                         /**< Delimiting character */
          size_t n_rows;                      /**< Number of rows in a file */
          size_t n_cols;                      /**< Number of columns in a CSV */
      };
  
      /** @name Shorthand Parsing Functions
       *  @brief Convienience functions for parsing small strings
       */
       ///@{
      CSVReader operator ""_csv(const char*, size_t);
      CSVReader operator ""_csv_no_header(const char*, size_t);
      CSVReader parse(csv::string_view in, CSVFormat format = CSVFormat());
      CSVReader parse_no_header(csv::string_view in);
      ///@}
  
      /** @name Utility Functions */
      ///@{
      std::unordered_map<std::string, DataType> csv_data_types(const std::string&);
      CSVFileInfo get_file_info(const std::string& filename);
      int get_col_pos(csv::string_view filename, csv::string_view col_name,
          const CSVFormat& format = CSVFormat::guess_csv());
      ///@}
  }
  /** @file
    *  A standalone header file for writing delimiter-separated files
    */
  
  #include <fstream>
  #include <iostream>
  #include <string>
  #include <tuple>
  #include <type_traits>
  #include <vector>
  
  
  namespace csv {
      namespace internals {
          /** to_string() for unsigned integers */
          template<typename T,
              csv::enable_if_t<std::is_unsigned<T>::value, int> = 0>
          inline std::string to_string(T value) {
              std::string digits_reverse = "";
  
              if (value == 0) return "0";
  
              while (value > 0) {
                  digits_reverse += (char)('0' + (value % 10));
                  value /= 10;
              }
  
              return std::string(digits_reverse.rbegin(), digits_reverse.rend());
          }
  
          /** to_string() for signed integers */
          template<
              typename T,
              csv::enable_if_t<std::is_integral<T>::value && std::is_signed<T>::value, int> = 0
          >
          inline std::string to_string(T value) {
              if (value >= 0)
                  return to_string((size_t)value);
  
              return "-" + to_string((size_t)(value * -1));
          }
  
          /** to_string() for floating point numbers */
          template<
              typename T,
              csv::enable_if_t<std::is_floating_point<T>::value, int> = 0
          >
          inline std::string to_string(T value) {
              std::string result;
  
              if (value < 0) result = "-";
              
              // Integral part
              size_t integral = (size_t)(std::abs(value));
              result += (integral == 0) ? "0" : to_string(integral);
  
              // Decimal part
              size_t decimal = (size_t)(((double)std::abs(value) - (double)integral) * 100000);
  
              result += ".";
              result += (decimal == 0) ? "0" : to_string(decimal);
  
              return result;
          }
      }
  
      /** @name CSV Writing */
      ///@{
      /** 
       *  Class for writing delimiter separated values files
       *
       *  To write formatted strings, one should
       *   -# Initialize a DelimWriter with respect to some output stream 
       *   -# Call write_row() on std::vector<std::string>s of unformatted text
       *
       *  @tparam OutputStream The output stream, e.g. `std::ofstream`, `std::stringstream`
       *  @tparam Delim        The delimiter character
       *  @tparam Quote        The quote character
       *
       *  @par Hint
       *  Use the aliases csv::CSVWriter<OutputStream> to write CSV
       *  formatted strings and csv::TSVWriter<OutputStream>
       *  to write tab separated strings
       *
       *  @par Example w/ std::vector, std::deque, std::list
       *  @snippet test_write_csv.cpp CSV Writer Example
       *
       *  @par Example w/ std::tuple
       *  @snippet test_write_csv.cpp CSV Writer Tuple Example
       */
      template<class OutputStream, char Delim, char Quote>
      class DelimWriter {
      public:
          /** Construct a DelimWriter over the specified output stream
           *
           *  @param  _out           Stream to write to
           *  @param  _quote_minimal Limit field quoting to only when necessary
          */
          DelimWriter(OutputStream& _out, bool _quote_minimal = true)
              : out(_out), quote_minimal(_quote_minimal) {};
  
          /** Construct a DelimWriter over the file
           *
           *  @param[out] filename  File to write to
           */
          DelimWriter(const std::string& filename) : DelimWriter(std::ifstream(filename)) {};
  
          /** Format a sequence of strings and write to CSV according to RFC 4180
           *
           *  @warning This does not check to make sure row lengths are consistent
           *
           *  @param[in]  record          Sequence of strings to be formatted
           *
           *  @return  The current DelimWriter instance (allowing for operator chaining)
           */
          template<typename T, size_t Size>
          DelimWriter& operator<<(const std::array<T, Size>& record) {
              for (size_t i = 0; i < Size; i++) {
                  out << csv_escape(record[i]);
                  if (i + 1 != Size) out << Delim;
              }
  
              out << std::endl;
              return *this;
          }
  
          /** @copydoc operator<< */
          template<typename... T>
          DelimWriter& operator<<(const std::tuple<T...>& record) {
              this->write_tuple<0, T...>(record);
              return *this;
          }
  
          /**
           * @tparam T A container such as std::vector, std::deque, or std::list
           * 
           * @copydoc operator<<
           */
          template<
              typename T, typename Alloc, template <typename, typename> class Container,
  
              // Avoid conflicting with tuples with two elements
              csv::enable_if_t<std::is_class<Alloc>::value, int> = 0
          >
              DelimWriter& operator<<(const Container<T, Alloc>& record) {
              const size_t ilen = record.size();
              size_t i = 0;
              for (const auto& field : record) {
                  out << csv_escape(field);
                  if (i + 1 != ilen) out << Delim;
                  i++;
              }
  
              out << std::endl;
              return *this;
          }
  
      private:
          template<
              typename T,
              csv::enable_if_t<
                  !std::is_convertible<T, std::string>::value
                  && !std::is_convertible<T, csv::string_view>::value
              , int> = 0
          >
          std::string csv_escape(T in) {
              return internals::to_string(in);
          }
  
          template<
              typename T,
              csv::enable_if_t<
                  std::is_convertible<T, std::string>::value
                  || std::is_convertible<T, csv::string_view>::value
              , int> = 0
          >
          std::string csv_escape(T in) {
              IF_CONSTEXPR(std::is_convertible<T, csv::string_view>::value) {
                  return _csv_escape(in);
              }
              
              return _csv_escape(std::string(in));
          }
  
          std::string _csv_escape(csv::string_view in) {
              /** Format a string to be RFC 4180-compliant
               *  @param[in]  in              String to be CSV-formatted
               *  @param[out] quote_minimal   Only quote fields if necessary.
               *                              If False, everything is quoted.
               */
  
              // Do we need a quote escape
              bool quote_escape = false;
  
              for (auto ch : in) {
                  if (ch == Quote || ch == Delim) {
                      quote_escape = true;
                      break;
                  }
              }
  
              if (!quote_escape) {
                  if (quote_minimal) return std::string(in);
                  else {
                      std::string ret(Quote, 1);
                      ret += in.data();
                      ret += Quote;
                  }
              }
  
              // Start initial quote escape sequence
              std::string ret(1, Quote);
              for (auto ch: in) {
                  if (ch == Quote) ret += std::string(2, Quote);
                  else ret += ch;
              }
  
              // Finish off quote escape
              ret += Quote;
              return ret;
          }
  
          /** Recurisve template for writing std::tuples */
          template<size_t Index = 0, typename... T>
          typename std::enable_if<Index < sizeof...(T), void>::type write_tuple(const std::tuple<T...>& record) {
              out << csv_escape(std::get<Index>(record));
  
              IF_CONSTEXPR (Index + 1 < sizeof...(T)) out << Delim;
  
              this->write_tuple<Index + 1>(record);
          }
  
          /** Base case for writing std::tuples */
          template<size_t Index = 0, typename... T>
          typename std::enable_if<Index == sizeof...(T), void>::type write_tuple(const std::tuple<T...>& record) {
              (void)record;
              out << std::endl;
          }
  
          OutputStream & out;
          bool quote_minimal;
      };
  
      /** An alias for csv::DelimWriter for writing standard CSV files
       *
       *  @sa csv::DelimWriter::operator<<()
       *
       *  @note Use `csv::make_csv_writer()` to in instatiate this class over
       *        an actual output stream.
       */
      template<class OutputStream>
      using CSVWriter = DelimWriter<OutputStream, ',', '"'>;
  
      /** Class for writing tab-separated values files
  *
       *  @sa csv::DelimWriter::write_row()
       *  @sa csv::DelimWriter::operator<<()
       *
       *  @note Use `csv::make_tsv_writer()` to in instatiate this class over
       *        an actual output stream.
       */
      template<class OutputStream>
      using TSVWriter = DelimWriter<OutputStream, '\t', '"'>;
  
      /** Return a csv::CSVWriter over the output stream */
      template<class OutputStream>
      inline CSVWriter<OutputStream> make_csv_writer(OutputStream& out, bool quote_minimal=true) {
          return CSVWriter<OutputStream>(out, quote_minimal);
      }
  
      /** Return a csv::TSVWriter over the output stream */
      template<class OutputStream>
      inline TSVWriter<OutputStream> make_tsv_writer(OutputStream& out, bool quote_minimal=true) {
          return TSVWriter<OutputStream>(out, quote_minimal);
      }
      ///@}
  }
  
  
  namespace csv {
      namespace internals {
          CSV_INLINE size_t get_file_size(csv::string_view filename) {
              std::ifstream infile(std::string(filename), std::ios::binary);
              const auto start = infile.tellg();
              infile.seekg(0, std::ios::end);
              const auto end = infile.tellg();
  
              return end - start;
          }
  
          CSV_INLINE std::string get_csv_head(csv::string_view filename) {
              return get_csv_head(filename, get_file_size(filename));
          }
  
          CSV_INLINE std::string get_csv_head(csv::string_view filename, size_t file_size) {
              const size_t bytes = 500000;
  
              std::error_code error;
              size_t length = std::min((size_t)file_size, bytes);
              auto mmap = mio::make_mmap_source(std::string(filename), 0, length, error);
  
              if (error) {
                  throw std::runtime_error("Cannot open file " + std::string(filename));
              }
  
              return std::string(mmap.begin(), mmap.end());
          }
  
  #ifdef _MSC_VER
  #pragma region IBasicCVParser
  #endif
          CSV_INLINE IBasicCSVParser::IBasicCSVParser(
              const CSVFormat& format,
              const ColNamesPtr& col_names
          ) : _col_names(col_names) {
              if (format.no_quote) {
                  _parse_flags = internals::make_parse_flags(format.get_delim());
              }
              else {
                  _parse_flags = internals::make_parse_flags(format.get_delim(), format.quote_char);
              }
  
              _ws_flags = internals::make_ws_flags(
                  format.trim_chars.data(), format.trim_chars.size()
              );
          }
  
          CSV_INLINE void IBasicCSVParser::end_feed() {
              using internals::ParseFlags;
  
              bool empty_last_field = this->data_ptr
                  && this->data_ptr->_data
                  && !this->data_ptr->data.empty()
                  && parse_flag(this->data_ptr->data.back()) == ParseFlags::DELIMITER;
  
              // Push field
              if (this->field_length > 0 || empty_last_field) {
                  this->push_field();
              }
  
              // Push row
              if (this->current_row.size() > 0)
                  this->push_row();
          }
  
          CSV_INLINE void IBasicCSVParser::parse_field() noexcept {
              using internals::ParseFlags;
              auto& in = this->data_ptr->data;
  
              // Trim off leading whitespace
              while (data_pos < in.size() && ws_flag(in[data_pos]))
                  data_pos++;
  
              if (field_start == UNINITIALIZED_FIELD)
                  field_start = (int)(data_pos - current_row_start());
  
              // Optimization: Since NOT_SPECIAL characters tend to occur in contiguous
              // sequences, use the loop below to avoid having to go through the outer
              // switch statement as much as possible
              while (data_pos < in.size() && compound_parse_flag(in[data_pos]) == ParseFlags::NOT_SPECIAL)
                  data_pos++;
  
              field_length = data_pos - (field_start + current_row_start());
  
              // Trim off trailing whitespace, this->field_length constraint matters
              // when field is entirely whitespace
              for (size_t j = data_pos - 1; ws_flag(in[j]) && this->field_length > 0; j--)
                  this->field_length--;
          }
  
          CSV_INLINE void IBasicCSVParser::push_field()
          {
              // Update
              if (field_has_double_quote) {
                  fields->emplace_back(
                      field_start == UNINITIALIZED_FIELD ? 0 : (unsigned int)field_start,
                      field_length,
                      true
                  );
                  field_has_double_quote = false;
  
              }
              else {
                  fields->emplace_back(
                      field_start == UNINITIALIZED_FIELD ? 0 : (unsigned int)field_start,
                      field_length
                  );
              }
  
              current_row.row_length++;
  
              // Reset field state
              field_start = UNINITIALIZED_FIELD;
              field_length = 0;
          }
  
          /** @return The number of characters parsed that belong to complete rows */
          CSV_INLINE size_t IBasicCSVParser::parse()
          {
              using internals::ParseFlags;
  
              this->quote_escape = false;
              this->data_pos = 0;
              this->current_row_start() = 0;
              this->trim_utf8_bom();
  
              auto& in = this->data_ptr->data;
              while (this->data_pos < in.size()) {
                  switch (compound_parse_flag(in[this->data_pos])) {
                  case ParseFlags::DELIMITER:
                      this->push_field();
                      this->data_pos++;
                      break;
  
                  case ParseFlags::NEWLINE:
                      this->data_pos++;
  
                      // Catches CRLF (or LFLF)
                      if (this->data_pos < in.size() && parse_flag(in[this->data_pos]) == ParseFlags::NEWLINE)
                          this->data_pos++;
  
                      // End of record -> Write record
                      this->push_field();
                      this->push_row();
  
                      // Reset
                      this->current_row = CSVRow(data_ptr, this->data_pos, fields->size());
                      break;
  
                  case ParseFlags::NOT_SPECIAL:
                      this->parse_field();
                      break;
  
                  case ParseFlags::QUOTE_ESCAPE_QUOTE:
                      if (data_pos + 1 == in.size()) return this->current_row_start();
                      else if (data_pos + 1 < in.size()) {
                          auto next_ch = parse_flag(in[data_pos + 1]);
                          if (next_ch >= ParseFlags::DELIMITER) {
                              quote_escape = false;
                              data_pos++;
                              break;
                          }
                          else if (next_ch == ParseFlags::QUOTE) {
                              // Case: Escaped quote
                              data_pos += 2;
                              this->field_length += 2;
                              this->field_has_double_quote = true;
                              break;
                          }
                      }
                      
                      // Case: Unescaped single quote => not strictly valid but we'll keep it
                      this->field_length++;
                      data_pos++;
  
                      break;
  
                  default: // Quote (currently not quote escaped)
                      if (this->field_length == 0) {
                          quote_escape = true;
                          data_pos++;
                          break;
                      }
  
                      // Case: Unescaped quote
                      this->field_length++;
                      data_pos++;
  
                      break;
                  }
              }
  
              return this->current_row_start();
          }
  
          CSV_INLINE void IBasicCSVParser::push_row() {
              current_row.row_length = fields->size() - current_row.fields_start;
              this->_records->push_back(std::move(current_row));
          }
  
          CSV_INLINE void IBasicCSVParser::reset_data_ptr() {
              this->data_ptr = std::make_shared<RawCSVData>();
              this->data_ptr->parse_flags = this->_parse_flags;
              this->data_ptr->col_names = this->_col_names;
              this->fields = &(this->data_ptr->fields);
          }
  
          CSV_INLINE void IBasicCSVParser::trim_utf8_bom() {
              auto& data = this->data_ptr->data;
  
              if (!this->unicode_bom_scan && data.size() >= 3) {
                  if (data[0] == '\xEF' && data[1] == '\xBB' && data[2] == '\xBF') {
                      this->data_pos += 3; // Remove BOM from input string
                      this->_utf8_bom = true;
                  }
  
                  this->unicode_bom_scan = true;
              }
          }
  #ifdef _MSC_VER
  #pragma endregion
  #endif
  
  #ifdef _MSC_VER
  #pragma region Specializations
  #endif
          CSV_INLINE void MmapParser::next(size_t bytes = ITERATION_CHUNK_SIZE) {
              // Reset parser state
              this->field_start = UNINITIALIZED_FIELD;
              this->field_length = 0;
              this->reset_data_ptr();
  
              // Create memory map
              size_t length = std::min(this->source_size - this->mmap_pos, bytes);
              std::error_code error;
              this->data_ptr->_data = std::make_shared<mio::basic_mmap_source<char>>(mio::make_mmap_source(this->_filename, this->mmap_pos, length, error));
              this->mmap_pos += length;
              if (error) throw error;
  
              auto mmap_ptr = (mio::basic_mmap_source<char>*)(this->data_ptr->_data.get());
  
              // Create string view
              this->data_ptr->data = csv::string_view(mmap_ptr->data(), mmap_ptr->length());
  
              // Parse
              this->current_row = CSVRow(this->data_ptr);
              size_t remainder = this->parse();            
  
              if (this->mmap_pos == this->source_size || no_chunk()) {
                  this->_eof = true;
                  this->end_feed();
              }
  
              this->mmap_pos -= (length - remainder);
          }
  #ifdef _MSC_VER
  #pragma endregion
  #endif
      }
  }
  
  namespace csv {
      namespace internals {
          CSV_INLINE std::vector<std::string> ColNames::get_col_names() const {
              return this->col_names;
          }
  
          CSV_INLINE void ColNames::set_col_names(const std::vector<std::string>& cnames) {
              this->col_names = cnames;
  
              for (size_t i = 0; i < cnames.size(); i++) {
                  this->col_pos[cnames[i]] = i;
              }
          }
  
          CSV_INLINE int ColNames::index_of(csv::string_view col_name) const {
              auto pos = this->col_pos.find(col_name.data());
              if (pos != this->col_pos.end())
                  return (int)pos->second;
  
              return CSV_NOT_FOUND;
          }
  
          CSV_INLINE size_t ColNames::size() const noexcept {
              return this->col_names.size();
          }
  
      }
  }
  /** @file
   *  Defines an object used to store CSV format settings
   */
  
  #include <algorithm>
  #include <set>
  
  
  namespace csv {
      CSV_INLINE CSVFormat& CSVFormat::delimiter(char delim) {
          this->possible_delimiters = { delim };
          this->assert_no_char_overlap();
          return *this;
      }
  
      CSV_INLINE CSVFormat& CSVFormat::delimiter(const std::vector<char> & delim) {
          this->possible_delimiters = delim;
          this->assert_no_char_overlap();
          return *this;
      }
  
      CSV_INLINE CSVFormat& CSVFormat::quote(char quote) {
          this->no_quote = false;
          this->quote_char = quote;
          this->assert_no_char_overlap();
          return *this;
      }
  
      CSV_INLINE CSVFormat& CSVFormat::trim(const std::vector<char> & chars) {
          this->trim_chars = chars;
          this->assert_no_char_overlap();
          return *this;
      }
  
      CSV_INLINE CSVFormat& CSVFormat::column_names(const std::vector<std::string>& names) {
          this->col_names = names;
          this->header = -1;
          return *this;
      }
  
      CSV_INLINE CSVFormat& CSVFormat::header_row(int row) {
          if (row < 0) this->variable_column_policy = VariableColumnPolicy::KEEP;
  
          this->header = row;
          this->col_names = {};
          return *this;
      }
  
      CSV_INLINE void CSVFormat::assert_no_char_overlap()
      {
          auto delims = std::set<char>(
              this->possible_delimiters.begin(), this->possible_delimiters.end()),
              trims = std::set<char>(
                  this->trim_chars.begin(), this->trim_chars.end());
  
          // Stores intersection of possible delimiters and trim characters
          std::vector<char> intersection = {};
  
          // Find which characters overlap, if any
          std::set_intersection(
              delims.begin(), delims.end(),
              trims.begin(), trims.end(),
              std::back_inserter(intersection));
  
          // Make sure quote character is not contained in possible delimiters
          // or whitespace characters
          if (delims.find(this->quote_char) != delims.end() ||
              trims.find(this->quote_char) != trims.end()) {
              intersection.push_back(this->quote_char);
          }
  
          if (!intersection.empty()) {
              std::string err_msg = "There should be no overlap between the quote character, "
                  "the set of possible delimiters "
                  "and the set of whitespace characters. Offending characters: ";
  
              // Create a pretty error message with the list of overlapping
              // characters
              for (size_t i = 0; i < intersection.size(); i++) {
                  err_msg += "'";
                  err_msg += intersection[i];
                  err_msg += "'";
  
                  if (i + 1 < intersection.size())
                      err_msg += ", ";
              }
  
              throw std::runtime_error(err_msg + '.');
          }
      }
  }
  /** @file
   *  @brief Defines functionality needed for basic CSV parsing
   */
  
  
  namespace csv {
      namespace internals {
          CSV_INLINE std::string format_row(const std::vector<std::string>& row, csv::string_view delim) {
              /** Print a CSV row */
              std::stringstream ret;
              for (size_t i = 0; i < row.size(); i++) {
                  ret << row[i];
                  if (i + 1 < row.size()) ret << delim;
                  else ret << std::endl;
              }
  
              return ret.str();
          }
  
          /** Return a CSV's column names
           *
           *  @param[in] filename  Path to CSV file
           *  @param[in] format    Format of the CSV file
           *
           */
          CSV_INLINE std::vector<std::string> _get_col_names(csv::string_view head, CSVFormat format) {
              // Parse the CSV
              auto trim_chars = format.get_trim_chars();
              std::stringstream source(head.data());
              RowCollection rows;
              
              StreamParser<std::stringstream> parser(source, format);
              parser.set_output(rows);
              parser.next();
  
              return CSVRow(std::move(rows[format.get_header()]));
          }
  
          CSV_INLINE GuessScore calculate_score(csv::string_view head, CSVFormat format) {
              // Frequency counter of row length
              std::unordered_map<size_t, size_t> row_tally = { { 0, 0 } };
  
              // Map row lengths to row num where they first occurred
              std::unordered_map<size_t, size_t> row_when = { { 0, 0 } };
  
              // Parse the CSV
              std::stringstream source(head.data());
              RowCollection rows;
  
              StreamParser<std::stringstream> parser(source, format);
              parser.set_output(rows);
              parser.next();
  
              for (size_t i = 0; i < rows.size(); i++) {
                  auto& row = rows[i];
  
                  // Ignore zero-length rows
                  if (row.size() > 0) {
                      if (row_tally.find(row.size()) != row_tally.end()) {
                          row_tally[row.size()]++;
                      }
                      else {
                          row_tally[row.size()] = 1;
                          row_when[row.size()] = i;
                      }
                  }
              }
  
              double final_score = 0;
              size_t header_row = 0;
  
              // Final score is equal to the largest 
              // row size times rows of that size
              for (auto& pair : row_tally) {
                  auto row_size = pair.first;
                  auto row_count = pair.second;
                  double score = (double)(row_size * row_count);
                  if (score > final_score) {
                      final_score = score;
                      header_row = row_when[row_size];
                  }
              }
  
              return {
                  final_score,
                  header_row
              };
          }
  
          /** Guess the delimiter used by a delimiter-separated values file */
          CSV_INLINE CSVGuessResult _guess_format(csv::string_view head, const std::vector<char>& delims) {
              /** For each delimiter, find out which row length was most common.
               *  The delimiter with the longest mode row length wins.
               *  Then, the line number of the header row is the first row with
               *  the mode row length.
               */
  
              CSVFormat format;
              size_t max_score = 0,
                  header = 0;
              char current_delim = delims[0];
  
              for (char cand_delim : delims) {
                  auto result = calculate_score(head, format.delimiter(cand_delim));
  
                  if (result.score > max_score) {
                      max_score = (size_t)result.score;
                      current_delim = cand_delim;
                      header = result.header;
                  }
              }
  
              return { current_delim, (int)header };
          }
      }
  
      /** Return a CSV's column names
       *
       *  @param[in] filename  Path to CSV file
       *  @param[in] format    Format of the CSV file
       *
       */
      CSV_INLINE std::vector<std::string> get_col_names(csv::string_view filename, CSVFormat format) {
          auto head = internals::get_csv_head(filename);
  
          /** Guess delimiter and header row */
          if (format.guess_delim()) {
              auto guess_result = guess_format(filename, format.get_possible_delims());
              format.delimiter(guess_result.delim).header_row(guess_result.header_row);
          }
  
          return internals::_get_col_names(head, format);
      }
  
      /** Guess the delimiter used by a delimiter-separated values file */
      CSV_INLINE CSVGuessResult guess_format(csv::string_view filename, const std::vector<char>& delims) {
          auto head = internals::get_csv_head(filename);
          return internals::_guess_format(head, delims);
      }
  
      /** Reads an arbitrarily large CSV file using memory-mapped IO.
       *
       *  **Details:** Reads the first block of a CSV file synchronously to get information
       *               such as column names and delimiting character.
       *
       *  @param[in] filename  Path to CSV file
       *  @param[in] format    Format of the CSV file
       *
       *  \snippet tests/test_read_csv.cpp CSVField Example
       *
       */
  	CSV_INLINE CSVReader::CSVReader(csv::string_view filename, CSVFormat format) : _format(format) {
          auto head = internals::get_csv_head(filename);
          using Parser = internals::MmapParser;
  
          /** Guess delimiter and header row */
          if (format.guess_delim()) {
              auto guess_result = internals::_guess_format(head, format.possible_delimiters);
              format.delimiter(guess_result.delim);
              format.header = guess_result.header_row;
              this->_format = format;
          }
  
          if (!format.col_names.empty())
              this->set_col_names(format.col_names);
  
          this->parser = std::unique_ptr<Parser>(new Parser(filename, format, this->col_names)); // For C++11
          this->initial_read();
      }
  
      /** Return the format of the original raw CSV */
      CSV_INLINE CSVFormat CSVReader::get_format() const {
          CSVFormat new_format = this->_format;
  
          // Since users are normally not allowed to set 
          // column names and header row simulatenously,
          // we will set the backing variables directly here
          new_format.col_names = this->col_names->get_col_names();
          new_format.header = this->_format.header;
  
          return new_format;
      }
  
      /** Return the CSV's column names as a vector of strings. */
      CSV_INLINE std::vector<std::string> CSVReader::get_col_names() const {
          if (this->col_names) {
              return this->col_names->get_col_names();
          }
  
          return std::vector<std::string>();
      }
  
      /** Return the index of the column name if found or
       *         csv::CSV_NOT_FOUND otherwise.
       */
      CSV_INLINE int CSVReader::index_of(csv::string_view col_name) const {
          auto _col_names = this->get_col_names();
          for (size_t i = 0; i < _col_names.size(); i++)
              if (_col_names[i] == col_name) return (int)i;
  
          return CSV_NOT_FOUND;
      }
  
      CSV_INLINE void CSVReader::trim_header() {
          if (!this->header_trimmed) {
              for (int i = 0; i <= this->_format.header && !this->records.empty(); i++) {
                  if (i == this->_format.header && this->col_names->empty()) {
                      this->set_col_names(this->records.pop_front());
                  }
                  else {
                      this->records.pop_front();
                  }
              }
  
              this->header_trimmed = true;
          }
      }
  
      /**
       *  @param[in] names Column names
       */
      CSV_INLINE void CSVReader::set_col_names(const std::vector<std::string>& names)
      {
          this->col_names->set_col_names(names);
          this->n_cols = names.size();
      }
  
      /**
       * Read a chunk of CSV data.
       *
       * @note This method is meant to be run on its own thread. Only one `read_csv()` thread
       *       should be active at a time.
       *
       * @param[in] bytes Number of bytes to read.
       *
       * @see CSVReader::read_csv_worker
       * @see CSVReader::read_row()
       */
      CSV_INLINE bool CSVReader::read_csv(size_t bytes) {
          // Tell read_row() to listen for CSV rows
          this->records.notify_all();
  
          this->parser->set_output(this->records);
          this->parser->next(bytes);
  
          if (!this->header_trimmed) {
              this->trim_header();
          }
  
          // Tell read_row() to stop waiting
          this->records.kill_all();
  
          return true;
      }
  
      /**
       * Retrieve rows as CSVRow objects, returning true if more rows are available.
       *
       * @par Performance Notes
       *  - Reads chunks of data that are csv::internals::ITERATION_CHUNK_SIZE bytes large at a time
       *  - For performance details, read the documentation for CSVRow and CSVField.
       *
       * @param[out] row The variable where the parsed row will be stored
       * @see CSVRow, CSVField
       *
       * **Example:**
       * \snippet tests/test_read_csv.cpp CSVField Example
       *
       */
      CSV_INLINE bool CSVReader::read_row(CSVRow &row) {
          while (true) {
              if (this->records.empty()) {
                  if (this->records.is_waitable())
                      // Reading thread is currently active => wait for it to populate records
                      this->records.wait();
                  else if (this->parser->eof())
                      // End of file and no more records
                      return false;
                  else {
                      // Reading thread is not active => start another one
                      if (this->read_csv_worker.joinable())
                          this->read_csv_worker.join();
  
                      this->read_csv_worker = std::thread(&CSVReader::read_csv, this, internals::ITERATION_CHUNK_SIZE);
                  }
              }
              else if (this->records.front().size() != this->n_cols &&
                  this->_format.variable_column_policy != VariableColumnPolicy::KEEP) {
                  auto errored_row = this->records.pop_front();
  
                  if (this->_format.variable_column_policy == VariableColumnPolicy::THROW) {
                      if (errored_row.size() < this->n_cols)
                          throw std::runtime_error("Line too short " + internals::format_row(errored_row));
  
                      throw std::runtime_error("Line too long " + internals::format_row(errored_row));
                  }
              }
              else {
                  row = std::move(this->records.pop_front());
                  this->_n_rows++;
                  return true;
              }
          }
      
          return false;
      }
  }
  /** @file
   *  Defines an input iterator for csv::CSVReader
   */
  
  
  namespace csv {
      /** Return an iterator to the first row in the reader */
      CSV_INLINE CSVReader::iterator CSVReader::begin() {
          if (this->records.empty()) {
              this->read_csv_worker = std::thread(&CSVReader::read_csv, this, internals::ITERATION_CHUNK_SIZE);
              this->read_csv_worker.join();
  
              // Still empty => return end iterator
              if (this->records.empty()) return this->end();
          }
  
          CSVReader::iterator ret(this, std::move(this->records.pop_front()));
          return ret;
      }
  
      /** A placeholder for the imaginary past the end row in a CSV.
       *  Attempting to deference this will lead to bad things.
       */
      CSV_INLINE HEDLEY_CONST CSVReader::iterator CSVReader::end() const noexcept {
          return CSVReader::iterator();
      }
  
      /////////////////////////
      // CSVReader::iterator //
      /////////////////////////
  
      CSV_INLINE CSVReader::iterator::iterator(CSVReader* _daddy, CSVRow&& _row) :
          daddy(_daddy) {
          row = std::move(_row);
      }
  
      /** Advance the iterator by one row. If this CSVReader has an
       *  associated file, then the iterator will lazily pull more data from
       *  that file until the end of file is reached.
       *
       *  @note This iterator does **not** block the thread responsible for parsing CSV.
       *
       */
      CSV_INLINE CSVReader::iterator& CSVReader::iterator::operator++() {
          if (!daddy->read_row(this->row)) {
              this->daddy = nullptr; // this == end()
          }
  
          return *this;
      }
  
      /** Post-increment iterator */
      CSV_INLINE CSVReader::iterator CSVReader::iterator::operator++(int) {
          auto temp = *this;
          if (!daddy->read_row(this->row)) {
              this->daddy = nullptr; // this == end()
          }
  
          return temp;
      }
  }
  /** @file
   *  Defines the data type used for storing information about a CSV row
   */
  
  #include <cassert>
  #include <functional>
  
  namespace csv {
      namespace internals {
          CSV_INLINE RawCSVField& CSVFieldList::operator[](size_t n) const {
              const size_t page_no = n / _single_buffer_capacity;
              const size_t buffer_idx = (page_no < 1) ? n : n % _single_buffer_capacity;
              return this->buffers[page_no][buffer_idx];
          }
  
          CSV_INLINE void CSVFieldList::allocate() {
              RawCSVField * buffer = new RawCSVField[_single_buffer_capacity];
              buffers.push_back(buffer);
              _current_buffer_size = 0;
              _back = &(buffers.back()[0]);
          }
      }
  
      /** Return a CSVField object corrsponding to the nth value in the row.
       *
       *  @note This method performs bounds checking, and will throw an
       *        `std::runtime_error` if n is invalid.
       *
       *  @complexity
       *  Constant, by calling csv::CSVRow::get_csv::string_view()
       *
       */
      CSV_INLINE CSVField CSVRow::operator[](size_t n) const {
          return CSVField(this->get_field(n));
      }
  
      /** Retrieve a value by its associated column name. If the column
       *  specified can't be round, a runtime error is thrown.
       *
       *  @complexity
       *  Constant. This calls the other CSVRow::operator[]() after
       *  converting column names into indices using a hash table.
       *
       *  @param[in] col_name The column to look for
       */
      CSV_INLINE CSVField CSVRow::operator[](const std::string& col_name) const {
          auto & col_names = this->data->col_names;
          auto col_pos = col_names->index_of(col_name);
          if (col_pos > -1) {
              return this->operator[](col_pos);
          }
  
          throw std::runtime_error("Can't find a column named " + col_name);
      }
  
      CSV_INLINE CSVRow::operator std::vector<std::string>() const {
          std::vector<std::string> ret;
          for (size_t i = 0; i < size(); i++)
              ret.push_back(std::string(this->get_field(i)));
  
          return ret;
      }
  
      CSV_INLINE csv::string_view CSVRow::get_field(size_t index) const
      {
          using internals::ParseFlags;
  
          if (index >= this->size())
              throw std::runtime_error("Index out of bounds.");
  
          const size_t field_index = this->fields_start + index;
          auto& field = this->data->fields[field_index];
          auto field_str = csv::string_view(this->data->data).substr(this->data_start + field.start);
  
          if (field.has_double_quote) {
              auto& value = this->data->double_quote_fields[field_index];
              if (value.empty()) {
                  bool prev_ch_quote = false;
                  for (size_t i = 0; i < field.length; i++) {
                      if (this->data->parse_flags[field_str[i] + 128] == ParseFlags::QUOTE) {
                          if (prev_ch_quote) {
                              prev_ch_quote = false;
                              continue;
                          }
                          else {
                              prev_ch_quote = true;
                          }
                      }
  
                      value += field_str[i];
                  }
              }
  
              return csv::string_view(value);
          }
  
          return field_str.substr(0, field.length);
      }
  
  #ifdef _MSC_VER
  #pragma region CSVRow Iterator
  #endif
      /** Return an iterator pointing to the first field. */
      CSV_INLINE CSVRow::iterator CSVRow::begin() const {
          return CSVRow::iterator(this, 0);
      }
  
      /** Return an iterator pointing to just after the end of the CSVRow.
       *
       *  @warning Attempting to dereference the end iterator results
       *           in dereferencing a null pointer.
       */
      CSV_INLINE CSVRow::iterator CSVRow::end() const noexcept {
          return CSVRow::iterator(this, (int)this->size());
      }
  
      CSV_INLINE CSVRow::reverse_iterator CSVRow::rbegin() const noexcept {
          return std::reverse_iterator<CSVRow::iterator>(this->end());
      }
  
      CSV_INLINE CSVRow::reverse_iterator CSVRow::rend() const {
          return std::reverse_iterator<CSVRow::iterator>(this->begin());
      }
  
      CSV_INLINE HEDLEY_NON_NULL(2)
      CSVRow::iterator::iterator(const CSVRow* _reader, int _i)
          : daddy(_reader), i(_i) {
          if (_i < (int)this->daddy->size())
              this->field = std::make_shared<CSVField>(
                  this->daddy->operator[](_i));
          else
              this->field = nullptr;
      }
  
      CSV_INLINE CSVRow::iterator::reference CSVRow::iterator::operator*() const {
          return *(this->field.get());
      }
  
      CSV_INLINE CSVRow::iterator::pointer CSVRow::iterator::operator->() const {
          // Using CSVField * as pointer type causes segfaults in MSVC debug builds
          #ifdef _MSC_BUILD
          return this->field;
          #else
          return this->field.get();
          #endif
      }
  
      CSV_INLINE CSVRow::iterator& CSVRow::iterator::operator++() {
          // Pre-increment operator
          this->i++;
          if (this->i < (int)this->daddy->size())
              this->field = std::make_shared<CSVField>(
                  this->daddy->operator[](i));
          else // Reached the end of row
              this->field = nullptr;
          return *this;
      }
  
      CSV_INLINE CSVRow::iterator CSVRow::iterator::operator++(int) {
          // Post-increment operator
          auto temp = *this;
          this->operator++();
          return temp;
      }
  
      CSV_INLINE CSVRow::iterator& CSVRow::iterator::operator--() {
          // Pre-decrement operator
          this->i--;
          this->field = std::make_shared<CSVField>(
              this->daddy->operator[](this->i));
          return *this;
      }
  
      CSV_INLINE CSVRow::iterator CSVRow::iterator::operator--(int) {
          // Post-decrement operator
          auto temp = *this;
          this->operator--();
          return temp;
      }
      
      CSV_INLINE CSVRow::iterator CSVRow::iterator::operator+(difference_type n) const {
          // Allows for iterator arithmetic
          return CSVRow::iterator(this->daddy, i + (int)n);
      }
  
      CSV_INLINE CSVRow::iterator CSVRow::iterator::operator-(difference_type n) const {
          // Allows for iterator arithmetic
          return CSVRow::iterator::operator+(-n);
      }
  #ifdef _MSC_VER
  #pragma endregion CSVRow Iterator
  #endif
  }
  
  /** @file
   *  Implements JSON serialization abilities
   */
  
  
  namespace csv {
      /*
      The implementations for json_extra_space() and json_escape_string()
      were modified from source code for JSON for Modern C++.
  
      The respective license is below:
  
      The code is licensed under the [MIT
      License](http://opensource.org/licenses/MIT):
      
      Copyright &copy; 2013-2015 Niels Lohmann.
      
      Permission is hereby granted, free of charge, to any person
      obtaining a copy of this software and associated documentation files
      (the "Software"), to deal in the Software without restriction,
      including without limitation the rights to use, copy, modify, merge,
      publish, distribute, sublicense, and/or sell copies of the Software,
      and to permit persons to whom the Software is furnished to do so,
      subject to the following conditions:
      
      The above copyright notice and this permission notice shall be
      included in all copies or substantial portions of the Software.
      
      THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
      EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
      MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
      NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
      BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
      ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
      CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
      SOFTWARE.
      */
  
      namespace internals {
          /*!
           @brief calculates the extra space to escape a JSON string
  
           @param[in] s  the string to escape
           @return the number of characters required to escape string @a s
  
           @complexity Linear in the length of string @a s.
          */
          static std::size_t json_extra_space(csv::string_view& s) noexcept
          {
              std::size_t result = 0;
  
  
              for (const auto& c : s)
              {
                  switch (c)
                  {
                  case '"':
                  case '\\':
                  case '\b':
                  case '\f':
                  case '\n':
                  case '\r':
                  case '\t':
                  {
                      // from c (1 byte) to \x (2 bytes)
                      result += 1;
                      break;
                  }
  
  
                  default:
                  {
                      if (c >= 0x00 && c <= 0x1f)
                      {
                          // from c (1 byte) to \uxxxx (6 bytes)
                          result += 5;
                      }
                      break;
                  }
                  }
              }
  
  
              return result;
          }
  
          CSV_INLINE std::string json_escape_string(csv::string_view s) noexcept
          {
              const auto space = json_extra_space(s);
              if (space == 0)
              {
                  return std::string(s);
              }
  
              // create a result string of necessary size
              std::string result(s.size() + space, '\\');
              std::size_t pos = 0;
  
              for (const auto& c : s)
              {
                  switch (c)
                  {
                  // quotation mark (0x22)
                  case '"':
                  {
                      result[pos + 1] = '"';
                      pos += 2;
                      break;
                  }
  
  
                  // reverse solidus (0x5c)
                  case '\\':
                  {
                      // nothing to change
                      pos += 2;
                      break;
                  }
  
  
                  // backspace (0x08)
                  case '\b':
                  {
                      result[pos + 1] = 'b';
                      pos += 2;
                      break;
                  }
  
  
                  // formfeed (0x0c)
                  case '\f':
                  {
                      result[pos + 1] = 'f';
                      pos += 2;
                      break;
                  }
  
  
                  // newline (0x0a)
                  case '\n':
                  {
                      result[pos + 1] = 'n';
                      pos += 2;
                      break;
                  }
  
  
                  // carriage return (0x0d)
                  case '\r':
                  {
                      result[pos + 1] = 'r';
                      pos += 2;
                      break;
                  }
  
  
                  // horizontal tab (0x09)
                  case '\t':
                  {
                      result[pos + 1] = 't';
                      pos += 2;
                      break;
                  }
  
  
                  default:
                  {
                      if (c >= 0x00 && c <= 0x1f)
                      {
                          // print character c as \uxxxx
                          sprintf(&result[pos + 1], "u%04x", int(c));
                          pos += 6;
                          // overwrite trailing null character
                          result[pos] = '\\';
                      }
                      else
                      {
                          // all other characters are added as-is
                          result[pos++] = c;
                      }
                      break;
                  }
                  }
              }
  
              return result;
          }
      }
  
      /** Convert a CSV row to a JSON object, i.e.
       *  `{"col1":"value1","col2":"value2"}`
       *
       *  @note All strings are properly escaped. Numeric values are not quoted.
       *  @param[in] subset A subset of columns to contain in the JSON.
       *                    Leave empty for original columns.
       */
      CSV_INLINE std::string CSVRow::to_json(const std::vector<std::string>& subset) const {
          std::vector<std::string> col_names = subset;
          if (subset.empty()) {
              col_names = this->data ? this->get_col_names() : std::vector<std::string>({});
          }
  
          const size_t _n_cols = col_names.size();
          std::string ret = "{";
          
          for (size_t i = 0; i < _n_cols; i++) {
              auto& col = col_names[i];
              auto field = this->operator[](col);
  
              // TODO: Possible performance enhancements by caching escaped column names
              ret += '"' + internals::json_escape_string(col) + "\":";
  
              // Add quotes around strings but not numbers
              if (field.is_num())
                   ret += internals::json_escape_string(field.get<csv::string_view>());
              else
                  ret += '"' + internals::json_escape_string(field.get<csv::string_view>()) + '"';
  
              // Do not add comma after last string
              if (i + 1 < _n_cols)
                  ret += ',';
          }
  
          ret += '}';
          return ret;
      }
  
      /** Convert a CSV row to a JSON array, i.e.
       *  `["value1","value2",...]`
       *
       *  @note All strings are properly escaped. Numeric values are not quoted.
       *  @param[in] subset A subset of columns to contain in the JSON.
       *                    Leave empty for all columns.
       */
      CSV_INLINE std::string CSVRow::to_json_array(const std::vector<std::string>& subset) const {
          std::vector<std::string> col_names = subset;
          if (subset.empty())
              col_names = this->data ? this->get_col_names() : std::vector<std::string>({});
  
          const size_t _n_cols = col_names.size();
          std::string ret = "[";
  
          for (size_t i = 0; i < _n_cols; i++) {
              auto field = this->operator[](col_names[i]);
  
              // Add quotes around strings but not numbers
              if (field.is_num())
                  ret += internals::json_escape_string(field.get<csv::string_view>());
              else
                  ret += '"' + internals::json_escape_string(field.get<csv::string_view>()) + '"';
  
              // Do not add comma after last string
              if (i + 1 < _n_cols)
                  ret += ',';
          }
  
          ret += ']';
          return ret;
      }
  }
  /** @file
   *  Calculates statistics from CSV files
   */
  
  #include <string>
  
  namespace csv {
      /** Calculate statistics for an arbitrarily large file. When this constructor
       *  is called, CSVStat will process the entire file iteratively. Once finished,
       *  methods like get_mean(), get_counts(), etc... can be used to retrieve statistics.
       */
      CSV_INLINE CSVStat::CSVStat(csv::string_view filename, CSVFormat format) :
          reader(filename, format) {
          this->calc();
      }
  
      /** Calculate statistics for a CSV stored in a std::stringstream */
      CSV_INLINE CSVStat::CSVStat(std::stringstream& stream, CSVFormat format) :
          reader(stream, format) {
          this->calc();
      }
  
      /** Return current means */
      CSV_INLINE std::vector<long double> CSVStat::get_mean() const {
          std::vector<long double> ret;        
          for (size_t i = 0; i < this->get_col_names().size(); i++) {
              ret.push_back(this->rolling_means[i]);
          }
          return ret;
      }
  
      /** Return current variances */
      CSV_INLINE std::vector<long double> CSVStat::get_variance() const {
          std::vector<long double> ret;        
          for (size_t i = 0; i < this->get_col_names().size(); i++) {
              ret.push_back(this->rolling_vars[i]/(this->n[i] - 1));
          }
          return ret;
      }
  
      /** Return current mins */
      CSV_INLINE std::vector<long double> CSVStat::get_mins() const {
          std::vector<long double> ret;        
          for (size_t i = 0; i < this->get_col_names().size(); i++) {
              ret.push_back(this->mins[i]);
          }
          return ret;
      }
  
      /** Return current maxes */
      CSV_INLINE std::vector<long double> CSVStat::get_maxes() const {
          std::vector<long double> ret;        
          for (size_t i = 0; i < this->get_col_names().size(); i++) {
              ret.push_back(this->maxes[i]);
          }
          return ret;
      }
  
      /** Get counts for each column */
      CSV_INLINE std::vector<CSVStat::FreqCount> CSVStat::get_counts() const {
          std::vector<FreqCount> ret;
          for (size_t i = 0; i < this->get_col_names().size(); i++) {
              ret.push_back(this->counts[i]);
          }
          return ret;
      }
  
      /** Get data type counts for each column */
      CSV_INLINE std::vector<CSVStat::TypeCount> CSVStat::get_dtypes() const {
          std::vector<TypeCount> ret;        
          for (size_t i = 0; i < this->get_col_names().size(); i++) {
              ret.push_back(this->dtypes[i]);
          }
          return ret;
      }
  
      CSV_INLINE void CSVStat::calc() {
          constexpr size_t CALC_CHUNK_SIZE = 5000;
  
          while (true) {
              /** Chunk rows */
              for (auto& row : reader) {
                  if (this->records.size() < CALC_CHUNK_SIZE) {
                      this->records.push_back(std::move(row));
                  }
              }
  
              /** Go through all records and calculate specified statistics */
              for (size_t i = 0; i < this->get_col_names().size(); i++) {
                  dtypes.push_back({});
                  counts.push_back({});
                  rolling_means.push_back(0);
                  rolling_vars.push_back(0);
                  mins.push_back(NAN);
                  maxes.push_back(NAN);
                  n.push_back(0);
              }
  
              std::vector<std::thread> pool;
  
              // Start threads
              for (size_t i = 0; i < this->get_col_names().size(); i++)
                  pool.push_back(std::thread(&CSVStat::calc_worker, this, i));
  
              // Block until done
              for (auto& th : pool)
                  th.join();
  
              if (reader.eof()) break;
          }
      }
  
      CSV_INLINE void CSVStat::calc_worker(const size_t &i) {
          /** Worker thread for CSVStat::calc() which calculates statistics for one column.
           * 
           *  @param[in] i Column index
           */
  
          auto current_record = this->records.begin();
  
          for (size_t processed = 0; current_record != this->records.end(); processed++) {
              if (current_record->size() == this->get_col_names().size()) {
                  auto current_field = (*current_record)[i];
  
                  // Optimization: Don't count() if there's too many distinct values in the first 1000 rows
                  if (processed < 1000 || this->counts[i].size() <= 500)
                      this->count(current_field, i);
  
                  this->dtype(current_field, i);
  
                  // Numeric Stuff
                  if (current_field.is_num()) {
                      long double x_n = current_field.get<long double>();
  
                      // This actually calculates mean AND variance
                      this->variance(x_n, i);
                      this->min_max(x_n, i);
                  }
              }
              else if (this->reader.get_format().get_variable_column_policy() == VariableColumnPolicy::THROW) {
                  throw std::runtime_error("Line has different length than the others " + internals::format_row(*current_record));
              }
  
              ++current_record;
          }
      }
  
      CSV_INLINE void CSVStat::dtype(CSVField& data, const size_t &i) {
          /** Given a record update the type counter
           *  @param[in]  record Data observation
           *  @param[out] i      The column index that should be updated
           */
          
          auto type = data.type();
          if (this->dtypes[i].find(type) !=
              this->dtypes[i].end()) {
              // Increment count
              this->dtypes[i][type]++;
          } else {
              // Initialize count
              this->dtypes[i].insert(std::make_pair(type, 1));
          }
      }
  
      CSV_INLINE void CSVStat::count(CSVField& data, const size_t &i) {
          /** Given a record update the frequency counter
           *  @param[in]  record Data observation
           *  @param[out] i      The column index that should be updated
           */
  
          auto item = data.get<std::string>();
  
          if (this->counts[i].find(item) !=
              this->counts[i].end()) {
              // Increment count
              this->counts[i][item]++;
          } else {
              // Initialize count
              this->counts[i].insert(std::make_pair(item, 1));
          }
      }
  
      CSV_INLINE void CSVStat::min_max(const long double &x_n, const size_t &i) {
          /** Update current minimum and maximum
           *  @param[in]  x_n Data observation
           *  @param[out] i   The column index that should be updated
           */
          if (std::isnan(this->mins[i]))
              this->mins[i] = x_n;
          if (std::isnan(this->maxes[i]))
              this->maxes[i] = x_n;
          
          if (x_n < this->mins[i])
              this->mins[i] = x_n;
          else if (x_n > this->maxes[i])
              this->maxes[i] = x_n;
      }
  
      CSV_INLINE void CSVStat::variance(const long double &x_n, const size_t &i) {
          /** Given a record update rolling mean and variance for all columns
           *  using Welford's Algorithm
           *  @param[in]  x_n Data observation
           *  @param[out] i   The column index that should be updated
           */
          long double& current_rolling_mean = this->rolling_means[i];
          long double& current_rolling_var = this->rolling_vars[i];
          long double& current_n = this->n[i];
          long double delta;
          long double delta2;
  
          current_n++;
          
          if (current_n == 1) {
              current_rolling_mean = x_n;
          } else {
              delta = x_n - current_rolling_mean;
              current_rolling_mean += delta/current_n;
              delta2 = x_n - current_rolling_mean;
              current_rolling_var += delta*delta2;
          }
      }
  
      /** Useful for uploading CSV files to SQL databases.
       *
       *  Return a data type for each column such that every value in a column can be
       *  converted to the corresponding data type without data loss.
       *  @param[in]  filename The CSV file
       *
       *  \return A mapping of column names to csv::DataType enums
       */
      CSV_INLINE std::unordered_map<std::string, DataType> csv_data_types(const std::string& filename) {
          CSVStat stat(filename);
          std::unordered_map<std::string, DataType> csv_dtypes;
  
          auto col_names = stat.get_col_names();
          auto temp = stat.get_dtypes();
  
          for (size_t i = 0; i < stat.get_col_names().size(); i++) {
              auto& col = temp[i];
              auto& col_name = col_names[i];
  
              if (col[DataType::CSV_STRING])
                  csv_dtypes[col_name] = DataType::CSV_STRING;
              else if (col[DataType::CSV_INT64])
                  csv_dtypes[col_name] = DataType::CSV_INT64;
              else if (col[DataType::CSV_INT32])
                  csv_dtypes[col_name] = DataType::CSV_INT32;
              else if (col[DataType::CSV_INT16])
                  csv_dtypes[col_name] = DataType::CSV_INT16;
              else if (col[DataType::CSV_INT8])
                  csv_dtypes[col_name] = DataType::CSV_INT8;
              else
                  csv_dtypes[col_name] = DataType::CSV_DOUBLE;
          }
  
          return csv_dtypes;
      }
  }
  #include <sstream>
  #include <vector>
  
  
  namespace csv {
      /** Shorthand function for parsing an in-memory CSV string
       *
       *  @return A collection of CSVRow objects
       *
       *  @par Example
       *  @snippet tests/test_read_csv.cpp Parse Example
       */
      CSV_INLINE CSVReader parse(csv::string_view in, CSVFormat format) {
          std::stringstream stream(in.data());
          return CSVReader(stream, format);
      }
  
      /** Parses a CSV string with no headers
       *
       *  @return A collection of CSVRow objects
       */
      CSV_INLINE CSVReader parse_no_header(csv::string_view in) {
          CSVFormat format;
          format.header_row(-1);
  
          return parse(in, format);
      }
  
      /** Parse a RFC 4180 CSV string, returning a collection
       *  of CSVRow objects
       *
       *  @par Example
       *  @snippet tests/test_read_csv.cpp Escaped Comma
       *
       */
      CSV_INLINE CSVReader operator ""_csv(const char* in, size_t n) {
          return parse(csv::string_view(in, n));
      }
  
      /** A shorthand for csv::parse_no_header() */
      CSV_INLINE CSVReader operator ""_csv_no_header(const char* in, size_t n) {
          return parse_no_header(csv::string_view(in, n));
      }
  
      /**
       *  Find the position of a column in a CSV file or CSV_NOT_FOUND otherwise
       *
       *  @param[in] filename  Path to CSV file
       *  @param[in] col_name  Column whose position we should resolve
       *  @param[in] format    Format of the CSV file
       */
      CSV_INLINE int get_col_pos(
          csv::string_view filename,
          csv::string_view col_name,
          const CSVFormat& format) {
          CSVReader reader(filename, format);
          return reader.index_of(col_name);
      }
  
      /** Get basic information about a CSV file
       *  @include programs/csv_info.cpp
       */
      CSV_INLINE CSVFileInfo get_file_info(const std::string& filename) {
          CSVReader reader(filename);
          CSVFormat format = reader.get_format();
          for (auto it = reader.begin(); it != reader.end(); ++it);
  
          CSVFileInfo info = {
              filename,
              reader.get_col_names(),
              format.get_delim(),
              reader.n_rows(),
              reader.get_col_names().size()
          };
  
          return info;
      }
  }
  
  
  #endif
  