//
// Copyright (c) 2016-2019 Vinnie Falco (vinnie dot falco at gmail dot com)
// Copyright (c) 2019-2020 Krystian Stasiowski (sdkrystian at gmail dot com)
//
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
//
// Official repository: https://github.com/boostorg/static_string
//

#ifndef BOOST_STATIC_STRING_STATIC_STRING_HPP
#define BOOST_STATIC_STRING_STATIC_STRING_HPP

#if defined(__GNUC__) && __GNUC__ >= 8
#pragma GCC diagnostic push
#pragma GCC system_header
// false positives
#pragma GCC diagnostic ignored "-Warray-bounds"
#pragma GCC diagnostic ignored "-Wrestrict"
#pragma GCC diagnostic ignored "-Wstringop-overflow"
#endif

#if defined(__GNUC__) && __GNUC__ >= 7
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnoexcept-type"
#endif

// External include guard
#ifndef BOOST_STATIC_STRING_CONFIG_HPP
#include <boost/static_string/config.hpp>
#include <boost/config/workaround.hpp>
#endif

#include <algorithm>
#include <cstdint>
#include <cstdio>
#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT)
#include <format>
#endif
#include <functional>
#include <initializer_list>
#include <limits>
#include <iosfwd>
#include <type_traits>
#include <utility>

namespace boost {
namespace static_strings {

#ifndef BOOST_STATIC_STRING_DOCS
template<std::size_t N, typename CharT, typename Traits>
class basic_static_string;

//------------------------------------------------------------------------------
//
// Aliases
//
//------------------------------------------------------------------------------

template<std::size_t N>
using static_string =
  basic_static_string<N, char, std::char_traits<char>>;

#ifdef BOOST_STATIC_STRING_HAS_WCHAR
template<std::size_t N>
using static_wstring =
  basic_static_string<N, wchar_t, std::char_traits<wchar_t>>;
#endif

template<std::size_t N>
using static_u16string =
  basic_static_string<N, char16_t, std::char_traits<char16_t>>;

template<std::size_t N>
using static_u32string =
  basic_static_string<N, char32_t, std::char_traits<char32_t>>;

#ifdef __cpp_char8_t
template<std::size_t N>
using static_u8string =
  basic_static_string<N, char8_t, std::char_traits<char8_t>>;
#endif

//--------------------------------------------------------------------------
//
// Detail
//
//--------------------------------------------------------------------------

namespace detail {

// Find the smallest width integral type that can hold a value as large as N (Glen Fernandes)
template<std::size_t N>
using smallest_width =
  typename std::conditional<(N <= (std::numeric_limits<unsigned char>::max)()), unsigned char,
  typename std::conditional<(N <= (std::numeric_limits<unsigned short>::max)()), unsigned short,
  typename std::conditional<(N <= (std::numeric_limits<unsigned int>::max)()), unsigned int,
  typename std::conditional<(N <= (std::numeric_limits<unsigned long>::max)()), unsigned long,
  typename std::conditional<(N <= (std::numeric_limits<unsigned long long>::max)()), unsigned long long,
  std::size_t>::type>::type>::type>::type>::type;

// std::is_nothrow_convertible is C++20
template<typename To>
void is_nothrow_convertible_helper(To) noexcept;

// MSVC is unable to parse this as a single expression, so a helper is needed
template<typename From, typename To, typename =
  decltype(is_nothrow_convertible_helper<To>(std::declval<From>()))>
struct is_nothrow_convertible_msvc_helper
{
  static const bool value =
    noexcept(is_nothrow_convertible_helper<To>(std::declval<From>()));
};

template<typename From, typename To, typename = void>
struct is_nothrow_convertible
  : std::false_type { };

template<typename From, typename To>
struct is_nothrow_convertible<From, To, typename std::enable_if<
  is_nothrow_convertible_msvc_helper<From, To>::value>::type>
    : std::true_type { };

// GCC 4.8, 4.9 workaround for void_t to make the defining-type-id dependant
template<typename...>
struct void_t_helper
{
  using type = void;
};

// void_t for c++11
template<typename... Ts>
using void_t = typename void_t_helper<Ts...>::type;

template <class T, typename CharT, typename = void>
struct is_string_like : std::false_type {};

template<typename T, typename CharT>
struct is_string_like<
    T, CharT,
    void_t<
      decltype(std::declval<CharT const*&>() = std::declval<T>().data()),
      decltype(std::declval<std::size_t&>() = std::declval<T>().size())>>
  : std::true_type
{};

// Check if a type can be used for templated
// overloads string_view_type
// This will be used by overloads that accept the string_view types
// directly and other convertible types such as std::string.
// When no string_view type is available, then we check for the
// data and size member functions, and use them directly for assignments.
// Types convertible to basic_static_string are not considered viewable
// to prevent any ambiguity during overload resolution.
template<std::size_t N, typename T, typename CharT, typename Traits, typename = void>
struct enable_if_viewable { };

template<std::size_t N, typename T, typename CharT, typename Traits>
struct enable_if_viewable<N, T, CharT, Traits,
    typename std::enable_if<
#if !defined(BOOST_STATIC_STRING_HAS_ANY_STRING_VIEW)
        is_string_like<T, CharT>::value &&
        !std::is_convertible<const T&, const basic_static_string<N, CharT, Traits>&>::value
#elif defined(BOOST_STATIC_STRING_STANDALONE)
        std::is_convertible<const T&, std::basic_string_view<CharT, Traits>>::value &&
        !std::is_convertible<const T&, const CharT*>::value &&
        !std::is_convertible<const T&, const basic_static_string<N, CharT, Traits>&>::value
#else
        (
            std::is_convertible<const T&, basic_string_view<CharT, Traits>>::value ||
            std::is_convertible<const T&, core::basic_string_view<CharT>>::value
        ) &&
        !std::is_convertible<const T&, const CharT*>::value &&
        !std::is_convertible<const T&, const basic_static_string<N, CharT, Traits>&>::value
#endif
    >::type>
{
  using type = void;
};

template<std::size_t N, typename T, typename CharT, typename Traits>
using enable_if_viewable_t = typename enable_if_viewable<N, T, CharT, Traits>::type;

// The common string_view type used in private operations with enable_if_viewable_t
// - T const& itself when no string_view type is available
// - basic_string_view (boost::string_view or std::string_view) when in
//   standalone because core::detail::string_view is unavailable
// - core::detail::basic_string_view otherwise because it's convertible
//   to and from most types, including std::string_view
// After converting a parameter to a common_string_view_type reference, we
// can use the data() and size() member functions.
#if !defined(BOOST_STATIC_STRING_HAS_ANY_STRING_VIEW)
template<typename T, typename CharT, typename Traits>
using common_string_view_type = T const&;
#elif defined(BOOST_STATIC_STRING_STANDALONE)
template<typename T, typename CharT, typename Traits>
using common_string_view_type = basic_string_view<CharT, Traits>;
#else
template <class T, typename CharT, typename Traits, typename = void>
struct common_string_view_type_impl {};

template<typename T, typename CharT, typename Traits>
struct common_string_view_type_impl<
    T, CharT, Traits,
    typename std::enable_if<
        is_string_like<T, CharT>::value &&
        !std::is_convertible<const T&, basic_string_view<CharT, Traits>>::value &&
        !std::is_convertible<const T&, core::basic_string_view<CharT>>::value>::type>
{
  using type = T const&;
};

template<typename T, typename CharT, typename Traits>
struct common_string_view_type_impl<
    T, CharT, Traits,
    typename std::enable_if<
        std::is_convertible<const T&, basic_string_view<CharT, Traits>>::value &&
        !std::is_convertible<const T&, core::basic_string_view<CharT>>::value>::type>
{
  using type = basic_string_view<CharT, Traits>;
};

template<typename T, typename CharT, typename Traits>
struct common_string_view_type_impl<
    T, CharT, Traits,
    typename std::enable_if<
        std::is_convertible<const T&, core::basic_string_view<CharT>>::value>::type>
{
  using type = core::basic_string_view<CharT>;
};

template<typename T, typename CharT, typename Traits>
using common_string_view_type = typename common_string_view_type_impl<T, CharT, Traits>::type;
#endif


// Simplified check for if a type is an iterator
template<typename T, typename = void>
struct is_iterator : std::false_type { };

template<typename T>
struct is_iterator<T,
  typename std::enable_if<std::is_class<T>::value,
    void_t<typename T::iterator_category>>::type>
      : std::true_type { };

template<typename T>
struct is_iterator<T*, void>
  : std::true_type { };

template<typename T, typename = void>
struct is_input_iterator : std::false_type { };

template<typename T>
struct is_input_iterator<T, typename std::enable_if<is_iterator<T>::value &&
  std::is_convertible<typename std::iterator_traits<T>::iterator_category,
    std::input_iterator_tag>::value>::type>
      : std::true_type { };

template<typename T, typename = void>
struct is_forward_iterator : std::false_type { };

template<typename T>
struct is_forward_iterator<T, typename std::enable_if<is_iterator<T>::value &&
  std::is_convertible<typename std::iterator_traits<T>::iterator_category,
    std::forward_iterator_tag>::value>::type>
      : std::true_type { };

template<typename T, typename = void>
struct is_subtractable
  : std::false_type { };

template<typename T>
struct is_subtractable<T, void_t<decltype(std::declval<T&>() - std::declval<T&>())>>
  : std::true_type { };

// constexpr distance for c++14
template<
  typename ForwardIt,
  typename std::enable_if<!is_subtractable<ForwardIt>::value>::type* = nullptr>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
std::size_t
distance(ForwardIt first, ForwardIt last)
{
  std::size_t dist = 0;
  for (; first != last; ++first, ++dist);
  return dist;
}

template<
  typename RandomIt,
  typename std::enable_if<is_subtractable<RandomIt>::value>::type* = nullptr>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
std::size_t
distance(RandomIt first, RandomIt last)
{
  return last - first;
}

// Copy using traits, respecting iterator rules
template<typename Traits, typename InputIt, typename CharT>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
void
copy_with_traits(
  InputIt first,
  InputIt last,
  CharT* out)
{
  for (; first != last; ++first, ++out)
    Traits::assign(*out, *first);
}

// Optimization for using the smallest possible type
template<std::size_t N, typename CharT, typename Traits>
class static_string_base
{
private:
  using size_type = smallest_width<N>;
  using value_type = typename Traits::char_type;
  using pointer = value_type*;
  using const_pointer = const value_type*;
public:
  BOOST_STATIC_STRING_CPP11_CONSTEXPR
  static_string_base() noexcept { };

  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  pointer
  data_impl() noexcept
  {
    return data_;
  }

  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  const_pointer
  data_impl() const noexcept
  {
    return data_;
  }

  BOOST_STATIC_STRING_CPP11_CONSTEXPR
  std::size_t
  size_impl() const noexcept
  {
    return size_;
  }

  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  std::size_t
  set_size(std::size_t n) noexcept
  {
    // Functions that set size will throw
    // if the new size would exceed max_size()
    // therefore we can guarantee that this will
    // not lose data.
    return size_ = size_type(n);
  }

  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  void
  term_impl() noexcept
  {
    Traits::assign(data_[size_], value_type());
  }

  size_type size_ = 0;

  value_type data_[N + 1]{};
};

// Optimization for when the size is 0
template<typename CharT, typename Traits>
class static_string_base<0, CharT, Traits>
{
private:
  using value_type = typename Traits::char_type;
  using pointer = value_type*;
public:
  BOOST_STATIC_STRING_CPP11_CONSTEXPR
  static_string_base() noexcept { }

  // Modifying the null terminator is UB
  BOOST_STATIC_STRING_CPP11_CONSTEXPR
  pointer
  data_impl() const noexcept
  {
    return const_cast<pointer>(&null_);
  }

  BOOST_STATIC_STRING_CPP11_CONSTEXPR
  std::size_t
  size_impl() const noexcept
  {
    return 0;
  }

  BOOST_STATIC_STRING_CPP11_CONSTEXPR
  std::size_t
  set_size(std::size_t) const noexcept
  {
    return 0;
  }

  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  void
  term_impl() const noexcept { }

private:
  static constexpr const value_type null_{};
};

// This is only needed in C++14 and lower.
// see http://eel.is/c++draft/depr.static.constexpr
#ifndef BOOST_STATIC_STRING_CPP17
template<typename CharT, typename Traits>
constexpr
const
typename static_string_base<0, CharT, Traits>::value_type
static_string_base<0, CharT, Traits>::
null_;
#endif

template<typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
int
lexicographical_compare(
  const CharT* s1,
  std::size_t n1,
  const CharT* s2,
  std::size_t n2) noexcept
{
  if(n1 < n2)
    return Traits::compare(
      s1, s2, n1) <= 0 ? -1 : 1;
  if(n1 > n2)
    return Traits::compare(
      s1, s2, n2) >= 0 ? 1 : -1;
  return Traits::compare(s1, s2, n1);
}

template<typename Traits, typename Integer>
inline
char*
integer_to_string(
  char* str_end,
  Integer value,
  std::true_type) noexcept
{
  if (value == 0)
  {
    Traits::assign(*--str_end, '0');
    return str_end;
  }
  if (value < 0)
  {
    const bool is_min = value == (std::numeric_limits<Integer>::min)();
    // negation of a min value cannot be represented
    if (is_min)
      value = (std::numeric_limits<Integer>::max)();
    else
      value = -value;
    const auto last_char = str_end - 1;
    for (; value > 0; value /= 10)
      Traits::assign(*--str_end, "0123456789"[value % 10]);
    // minimum values are powers of 2, so it will
    // never terminate with a 9.
    if (is_min)
      Traits::assign(*last_char, Traits::to_char_type(
        Traits::to_int_type(*last_char) + 1));
    Traits::assign(*--str_end, '-');
    return str_end;
  }
  for (; value > 0; value /= 10)
    Traits::assign(*--str_end, "0123456789"[value % 10]);
  return str_end;
}

template<typename Traits, typename Integer>
inline
char*
integer_to_string(
  char* str_end,
  Integer value,
  std::false_type) noexcept
{
  if (value == 0)
  {
    Traits::assign(*--str_end, '0');
    return str_end;
  }
  for (; value > 0; value /= 10)
    Traits::assign(*--str_end, "0123456789"[value % 10]);
  return str_end;
}

template<typename Traits, typename Integer>
inline
wchar_t*
integer_to_wstring(
  wchar_t* str_end,
  Integer value,
  std::true_type) noexcept
{
  if (value == 0)
  {
    Traits::assign(*--str_end, L'0');
    return str_end;
  }
  if (value < 0)
  {
    const bool is_min = value == (std::numeric_limits<Integer>::min)();
    // negation of a min value cannot be represented
    if (is_min)
      value = (std::numeric_limits<Integer>::max)();
    else
      value = -value;
    const auto last_char = str_end - 1;
    for (; value > 0; value /= 10)
      Traits::assign(*--str_end, L"0123456789"[value % 10]);
    // minimum values are powers of 2, so it will
    // never terminate with a 9.
    if (is_min)
      Traits::assign(*last_char, Traits::to_char_type(
        Traits::to_int_type(*last_char) + 1));
    Traits::assign(*--str_end, L'-');
    return str_end;
  }
  for (; value > 0; value /= 10)
    Traits::assign(*--str_end, L"0123456789"[value % 10]);
  return str_end;
}

template<typename Traits, typename Integer>
inline
wchar_t*
integer_to_wstring(
  wchar_t* str_end,
  Integer value,
  std::false_type) noexcept
{
  if (value == 0)
  {
    Traits::assign(*--str_end, L'0');
    return str_end;
  }
  for (; value > 0; value /= 10)
    Traits::assign(*--str_end, L"0123456789"[value % 10]);
  return str_end;
}

template<std::size_t N, typename Integer>
inline
static_string<N>
to_static_string_int_impl(Integer value) noexcept
{
  using size_type = typename static_string<N>::size_type;
  static_string<N> result;
  result.resize_and_overwrite(
    N, 
    [&](char* buffer, size_type) -> size_type
    {
      char* const digits_end = buffer + N;
      char* const digits_begin = integer_to_string<std::char_traits<char>, Integer>(
          digits_end, value, std::is_signed<Integer>{});
      const size_type len = digits_end - digits_begin;
      std::char_traits<char>::move(buffer, digits_begin, len);
      return len;
    }
  );

  return result;
}

#ifdef BOOST_STATIC_STRING_HAS_WCHAR
template<std::size_t N, typename Integer>
inline
static_wstring<N>
to_static_wstring_int_impl(Integer value) noexcept
{
  using size_type = typename static_wstring<N>::size_type;
  static_wstring<N> result;
  result.resize_and_overwrite(
    N,
    [&](wchar_t* buffer, size_type) -> size_type
    {
      wchar_t* const digits_end = buffer + N;
      wchar_t* const digits_begin = integer_to_wstring<std::char_traits<wchar_t>, Integer>(
          digits_end, value, std::is_signed<Integer>{});
      const size_type len = digits_end - digits_begin;
      std::char_traits<wchar_t>::move(buffer, digits_begin, len);
      return len;
    }
  );
  return result;
}
#endif

BOOST_STATIC_STRING_CPP11_CONSTEXPR
inline
int
count_digits(std::size_t value)
{
  return value < 10 ? 1 : count_digits(value / 10) + 1;
}

// Ignore -Wformat-truncation, we know what
// we are doing here. The version check does
// not need to be extremely precise.
#if defined(__GNUC__) && __GNUC__ >= 7
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wformat-truncation"
#endif

#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT)

template<std::size_t N, typename FloatingPoint>
inline
static_string<N>
cpp26_to_static_string(FloatingPoint value) noexcept
{
  using size_type = typename static_string<N>::size_type;
  static_string<N> result;
  result.resize_and_overwrite(
    N,
    [&](char* buffer, size_type) -> size_type
    {
      const auto formatted = std::format_to_n(buffer, N, "{}", value);
      return formatted.size;
    }
  );
  return result;
}

#endif

template<std::size_t N>
inline
static_string<N>
to_static_string_float_impl(double value) noexcept
{
#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT)
  return cpp26_to_static_string<N>(value);
#else
  using size_type = typename static_string<N>::size_type;
  static_string<N> result;
  // we have to assume here that no reasonable implementation
  // will require more than 2^63 chars to represent a float value.
  const long long narrow =
    static_cast<long long>(N);
  result.resize_and_overwrite(
    N,
    [&](char* buffer, size_type) -> size_type
    {
      // we know that a formatting error will not occur, so
      // we assume that the result is always positive
      std::size_t length = std::snprintf(buffer, N + 1, "%f", value);
      if (length > N)
      {
        // the + 4 is for the decimal, 'e',
        // its sign, and the sign of the integral portion
        const int reserved_count =
            (std::max)(2, count_digits(
                std::numeric_limits<double>::max_exponent10)) + 4;
        const int precision = narrow > reserved_count ?
            N - reserved_count : 0;
        // switch to scientific notation
        length = std::snprintf(buffer, N + 1, "%.*e", precision, value);
      }
      return length;
    }
  );
  return result;
#endif
}

template<std::size_t N>
inline
static_string<N>
to_static_string_float_impl(float value) noexcept
{
#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT)
  return cpp26_to_static_string<N>(value);
#else
  return to_static_string_float_impl<N>(static_cast<double>(value));
#endif
}

template<std::size_t N>
inline
static_string<N>
to_static_string_float_impl(long double value) noexcept
{
#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT)
  return cpp26_to_static_string<N>(value);
#else
  using size_type = typename static_string<N>::size_type;
  static_string<N> result;
  // we have to assume here that no reasonable implementation
  // will require more than 2^63 chars to represent a float value.
  const long long narrow =
    static_cast<long long>(N);
  result.resize_and_overwrite(
    N,
    [&](char* buffer, size_type)->size_type
    {
      // snprintf returns the number of characters
      // that would have been written
      // we know that a formatting error will not occur, so
      // we assume that the result is always positive
      std::size_t length = std::snprintf(buffer, N + 1, "%Lf", value);
      if (length > N)
      {
          // the + 4 is for the decimal, 'e',
          // its sign, and the sign of the integral portion
          const int reserved_count =
              (std::max)(2, count_digits(
                  std::numeric_limits<long double>::max_exponent10)) + 4;
          const int precision = narrow > reserved_count ?
              N - reserved_count : 0;
          // switch to scientific notation
          length = std::snprintf(buffer, N + 1, "%.*Le", precision, value);
      }
      return length;
    }
  );
  return result;
#endif
}

#ifdef BOOST_STATIC_STRING_HAS_WCHAR

#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT)
template<std::size_t N, typename FloatingPoint>
inline
static_wstring<N>
cpp26_to_static_wstring(FloatingPoint value) noexcept
{
  using size_type = typename static_wstring<N>::size_type;
  static_wstring<N> result;
  result.resize_and_overwrite(
    N,
    [&](wchar_t* buffer, size_type) -> size_type
    {
      const auto formatted = std::format_to_n(buffer, N, L"{}", value);
      return formatted.size;
    }
  );
  return result;
}

#endif

template<std::size_t N>
inline
static_wstring<N>
to_static_wstring_float_impl(double value) noexcept
{
#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT)
  return cpp26_to_static_wstring<N>(value);
#else
  using size_type = typename static_wstring<N>::size_type;
  static_wstring<N> result;
  // we have to assume here that no reasonable implementation
  // will require more than 2^63 chars to represent a float value.
  const long long narrow =
    static_cast<long long>(N);
  result.resize_and_overwrite(
    N,
    [&](wchar_t* buffer, size_type) -> size_type
    {
      // swprintf returns a negative number if it can't
      // fit all the characters in the buffer.
      // mingw has a non-standard swprintf, so
      // this just covers all the bases. short
      // circuit evaluation will ensure that the
      // second operand is not evaluated on conforming
      // implementations.
      int num_written =
          std::swprintf(buffer, N + 1, L"%f", value);
      if (num_written < 0 ||
          num_written > narrow)
      {
          // the + 4 is for the decimal, 'e',
          // its sign, and the sign of the integral portion
          const int reserved_count =
              (std::max)(2, count_digits(
                  std::numeric_limits<double>::max_exponent10)) + 4;
          const int precision = narrow > reserved_count ?
              N - reserved_count : 0;
          // switch to scientific notation
          num_written = std::swprintf(buffer, N + 1, L"%.*e", precision, value);
      }
      return num_written;
    }
  );
  return result;
#endif
}

template<std::size_t N>
inline
static_wstring<N>
to_static_wstring_float_impl(float value) noexcept
{
#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT)
  return cpp26_to_static_wstring<N>(value);
#else
    return to_static_wstring_float_impl<N>(static_cast<double>(value));
#endif
}

template<std::size_t N>
inline
static_wstring<N>
to_static_wstring_float_impl(long double value) noexcept
{
#if defined(BOOST_STATIC_STRING_USE_STD_FORMAT)
  return cpp26_to_static_wstring<N>(value);
#else
  using size_type = typename static_wstring<N>::size_type;
  static_wstring<N> result;
  // we have to assume here that no reasonable implementation
  // will require more than 2^63 chars to represent a float value.
  const long long narrow =
    static_cast<long long>(N);
  result.resize_and_overwrite(
    N,
    [&](wchar_t* buffer, size_type) -> size_type
    {
      // swprintf returns a negative number if it can't
      // fit all the characters in the buffer.
      // mingw has a non-standard swprintf, so
      // this just covers all the bases. short
      // circuit evaluation will ensure that the
      // second operand is not evaluated on conforming
      // implementations.
      int num_written =
          std::swprintf(buffer, N + 1, L"%Lf", value);
      if (num_written < 0 ||
          num_written > narrow)
      {
          // the + 4 is for the decimal, 'e',
          // its sign, and the sign of the integral portion
          const int reserved_count =
              (std::max)(2, count_digits(
                  std::numeric_limits<long double>::max_exponent10)) + 4;
          const int precision = narrow > reserved_count ?
              N - reserved_count : 0;
          // switch to scientific notation
          num_written = std::swprintf(buffer, N + 1, L"%.*Le", precision, value);
      }
      return num_written;
    }
  );
  return result;
#endif
}
#endif

#if defined(__GNUC__) && __GNUC__ >= 7
#pragma GCC diagnostic pop
#endif

template<typename Traits, typename CharT, typename ForwardIterator>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
ForwardIterator
find_not_of(
  ForwardIterator first,
  ForwardIterator last,
  const CharT* str,
  std::size_t n) noexcept
{
  for (; first != last; ++first)
    if (!Traits::find(str, n, *first))
      return first;
  return last;
}

// constexpr search for C++14
template<typename ForwardIt1, typename ForwardIt2, typename BinaryPredicate>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
ForwardIt1
search(
  ForwardIt1 first,
  ForwardIt1 last,
  ForwardIt2 s_first,
  ForwardIt2 s_last,
  BinaryPredicate p)
{
  for (; ; ++first)
  {
    ForwardIt1 it = first;
    for (ForwardIt2 s_it = s_first; ; ++it, ++s_it)
    {
      if (s_it == s_last)
        return first;
      if (it == last)
        return last;
      if (!p(*it, *s_it))
        break;
    }
  }
}

template<typename InputIt, typename ForwardIt, typename BinaryPredicate>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
InputIt
find_first_of(
  InputIt first,
  InputIt last,
  ForwardIt s_first,
  ForwardIt s_last,
  BinaryPredicate p)
{
  for (; first != last; ++first)
    for (ForwardIt it = s_first; it != s_last; ++it)
      if (p(*first, *it))
        return first;
  return last;
}

// KRYSTIAN TODO: add a constexpr rotate

// Check if a pointer lies within the range {src_first, src_last)
// without unspecified behavior, allowing it to be used
// in a constant evaluation.
template<typename T>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
ptr_in_range(
  const T* src_first,
  const T* src_last,
  const T* ptr)
{
#if defined(BOOST_STATIC_STRING_CPP14) && \
defined(BOOST_STATIC_STRING_IS_CONST_EVAL)
  // Our second best option is to use is_constant_evaluated
  // and a loop that checks for equality, since equality for
  // pointer to object types is never unspecified in this case.
  if (BOOST_STATIC_STRING_IS_CONST_EVAL)
  {
    for (; src_first != src_last; ++src_first)
      if (ptr == src_first)
        return true;
    return false;
  }
#endif
  // We want to make this usable in constant expressions as much as possible
  // while retaining the guarentee that the comparison has a strict total ordering.
  // We also want this to be fast. Since different compilers have differing levels
  // of conformance, we will settle for the best option that is available.
  // We don't care about this in C++11, since this function would have
  // no applications in constant expressions.
#if defined(BOOST_STATIC_STRING_CPP14) && \
defined(BOOST_STATIC_STRING_NO_PTR_COMP_FUNCTIONS)
  // If library comparison functions don't work,
  // we can use try builtin comparison operators instead.
  return ptr >= src_first && ptr < src_last;
#else
  // Use the library comparison functions if we can't use
  // is_constant_evaluated or if we don't need to.
  return std::greater_equal<const T*>()(ptr, src_first) &&
    std::less<const T*>()(ptr, src_last);
#endif
}

// This workaround is for gcc 5,
// which prohibits throw expressions in constexpr
// functions, but for some reason permits them in
// constructors.
#ifdef BOOST_STATIC_STRING_GCC5_BAD_CONSTEXPR
template<typename Exception>
struct throw_exception
{
  BOOST_STATIC_STRING_NORETURN
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  throw_exception(const char* msg)
  {
    BOOST_STATIC_STRING_THROW(Exception(msg));
  }
};
#else
template<typename Exception>
BOOST_STATIC_STRING_NORETURN
inline
void
throw_exception(const char* msg)
{
  BOOST_STATIC_STRING_THROW(Exception(msg));
}
#endif

} // detail
#endif

//--------------------------------------------------------------------------
//
// static_string
//
//--------------------------------------------------------------------------

/** A fixed-capacity string.

    These objects behave like `std::string` except that the storage
    is not dynamically allocated but rather fixed in size, and
    stored in the object itself.

    These strings offer performance advantages when an algorithm
    can execute with a reasonable upper limit on the size of a value.

    @par Aliases

    The following alias templates are provided for convenience:

    @code
    template<std::size_t N>
    using static_string =
      basic_static_string<N, char, std::char_traits<char>>;
    @endcode

    @code
    template<std::size_t N>
    using static_wstring =
      basic_static_string<N, wchar_t, std::char_traits<wchar_t>>;
    @endcode

    @code
    template<std::size_t N>
    using static_u16string =
      basic_static_string<N, char16_t, std::char_traits<char16_t>>;
    @endcode

    @code
    template<std::size_t N>
    using static_u32string =
      basic_static_string<N, char32_t, std::char_traits<char32_t>>;
    @endcode

    Addtionally, the alias template `static_u8string` is provided in C++20

    @code
    template<std::size_t N>
    using static_u8string =
      basic_static_string<N, char8_t, std::char_traits<char8_t>>;
    @endcode

    @see @ref to_static_string.
*/
template<std::size_t N, typename CharT,
  typename Traits = std::char_traits<CharT>>
class basic_static_string
#ifndef BOOST_STATIC_STRING_DOCS
  : private detail::static_string_base<N, CharT, Traits>
#endif
{
private:
  template<std::size_t, class, class>
  friend class basic_static_string;

public:
  //--------------------------------------------------------------------------
  //
  // Member types
  //
  //--------------------------------------------------------------------------

  /// The traits type.
  using traits_type = Traits;

  /// The character type.
  using value_type = typename traits_type::char_type;

  /// The size type.
  using size_type = std::size_t;

  /// The difference type.
  using difference_type = std::ptrdiff_t;

  /// The pointer type.
  using pointer = value_type*;

  /// The reference type.
  using reference = value_type&;

  /// The constant pointer type.
  using const_pointer = const value_type*;

  /// The constant reference type.
  using const_reference = const value_type&;

  /// The iterator type.
  using iterator = value_type*;

  /// The constant iterator type.
  using const_iterator = const value_type*;

  /// The reverse iterator type.
  using reverse_iterator =
    std::reverse_iterator<iterator>;

  /// The constant reverse iterator type.
  using const_reverse_iterator =
    std::reverse_iterator<const_iterator>;

#ifdef BOOST_STATIC_STRING_HAS_ANY_STRING_VIEW
  /// The string view type.
  using string_view_type =
    basic_string_view<value_type, traits_type>;
#endif

  //--------------------------------------------------------------------------
  //
  // Constants
  //
  //--------------------------------------------------------------------------

  /// Maximum size of the string excluding any null terminator
  static constexpr size_type static_capacity = N;

  /// A special index
  static constexpr size_type npos = size_type(-1);

  //--------------------------------------------------------------------------
  //
  // Construction
  //
  //--------------------------------------------------------------------------

  /** Constructor.

      Construct an empty string
  */
  BOOST_STATIC_STRING_CPP11_CONSTEXPR
  basic_static_string() noexcept
  {
#ifdef BOOST_STATIC_STRING_CPP20
    term();
#endif
  }

  /** Constructor.

      Construct the string with `count` copies of character `ch`.

      The behavior is undefined if `count >= npos`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string(
    size_type count,
    value_type ch)
  {
    assign(count, ch);
  }

  /** Constructor.

      Construct with a substring (pos, other.size()) of `other`.
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string(
    const basic_static_string<M, CharT, Traits>& other,
    size_type pos)
  {
    assign(other, pos);
  }

  /** Constructor.

      Construct with a substring (pos, count) of `other`.
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string(
    const basic_static_string<M, CharT, Traits>& other,
    size_type pos,
    size_type count)
  {
    assign(other, pos, count);
  }

  /** Constructor.

      Construct with the first `count` characters of `s`, including nulls.
    */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string(
    const_pointer s,
    size_type count)
  {
    assign(s, count);
  }

  /** Constructor.

      Construct from a null terminated string.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string(const_pointer s)
  {
    assign(s);
  }

  /** Constructor.

      Construct from a range of characters
  */
  template<typename InputIterator
#ifndef BOOST_STATIC_STRING_DOCS
    , typename std::enable_if<
      detail::is_input_iterator<InputIterator>
        ::value>::type* = nullptr
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string(
    InputIterator first,
    InputIterator last)
  {
    // KRYSTIAN TODO: we can use a better algorithm if this is a forward iterator
    assign(first, last);
  }

  /** Constructor.

      Copy constructor.
  */
  basic_static_string(const basic_static_string& other) = default;

  /** Constructor.

      Copy constructor.
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string(
    const basic_static_string<M, CharT, Traits>& other)
  {
    assign(other);
  }

  /** Constructor.

      Construct from an initializer list
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string(std::initializer_list<value_type> init)
  {
    assign(init.begin(), init.size());
  }

  /** Constructor.

      Construct from a object convertible to `string_view_type`
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  explicit
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string(const T& t)
  {
    assign(t);
  }

  /** Constructor.

      Construct from any object convertible to `string_view_type`.

      The range (pos, n) is extracted from the value
      obtained by converting `t` to `string_view_type`,
      and used to construct the string.
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string(
    const T& t,
    size_type pos,
    size_type n)
  {
    assign(t, pos, n);
  }

  //--------------------------------------------------------------------------
  //
  // Assignment
  //
  //--------------------------------------------------------------------------

  /** Assign to the string.

      Replaces the contents with those of
      the string `s`.

      @par Complexity

      Linear in `s.size()`.

      @par Exception Safety

      Strong guarantee.

      @return `*this`

      @param s The string to replace
      the contents with.

      @throw std::length_error `s.size() > max_size()`.
  */
  basic_static_string&
  operator=(const basic_static_string& s) = default;

  /** Assign to the string.

      Replaces the contents with those of
      the string `s`.

      @par Complexity

      Linear in `s.size()`.

      @par Exception Safety

      Strong guarantee.

      @tparam M The size of the other string.

      @return `*this`

      @param s The string to replace
      the contents with.

      @throw std::length_error `s.size() > max_size()`.
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  operator=(const basic_static_string<M, CharT, Traits>& s)
  {
    return assign(s);
  }

  /** Assign to the string.

      Replaces the contents with those of
      `{s, s + traits_type::length(s))`.

      @par Complexity

      Linear in `count`.

      @par Exception Safety

      Strong guarantee.

      @return `*this`

      @param s A pointer to the string to copy from.

      @throw std::length_error `traits_type::length(s) > max_size()`.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  operator=(const_pointer s)
  {
    return assign(s);
  }

  /** Assign to the string.

      Replaces the contents with a single copy of
      the character `ch`.

      @par Complexity

      Constant.

      @par Exception Safety

      Strong guarantee.

      @return `*this`

      @param ch The character to assign to.

      @throw std::length_error `count > max_size()`.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  operator=(value_type ch)
  {
    return assign_char(ch,
      std::integral_constant<bool, (N > 0)>{});
  }

  /** Assign to the string.

      Replaces the contents with those of the
      initializer list `ilist`.

      @par Complexity

      Linear in `init.size()`.

      @par Exception Safety

      Strong guarantee.

      @return `*this`

      @param ilist The initializer list to copy from.

      @throw std::length_error `ilist.size() > max_size()`.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  operator=(std::initializer_list<value_type> ilist)
  {
    return assign(ilist);
  }

  /** Assign to the string.

      Replaces the contents with those of
      `sv`, where `sv` is `string_view_type(t)`.

      @par Complexity

      Linear in `sv.size()`.

      @par Exception Safety

      Strong guarantee.

      @note

      The view can contain null characters.

      @tparam T A type convertible to `string_view_type`.

      @par Constraints

      @code
      std::is_convertible<const T&, string_view>::value &&
      !std::is_convertible<const T&, const CharT*>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value
      @endcode

      @return `*this`

      @param t The object to assign from.

      @throw std::length_error `sv.size() > max_size()`.
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  operator=(const T& t)
  {
    return assign(t);
  }

  /** Assign to the string.

      Replaces the contents with `count` copies of
      character `ch`.

      @par Complexity

      Linear in `count`.

      @par Exception Safety

      Strong guarantee.

      @return `*this`

      @param count The size of the resulting string.

      @param ch The value to initialize characters
      of the string with.

      @throw std::length_error `count > max_size()`.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  assign(
    size_type count,
    value_type ch);

  /** Assign to the string.

      Replaces the contents with those of
      the string `s`.

      @par Complexity

      Linear in `s.size()`.

      @par Exception Safety

      Strong guarantee.

      @tparam M The size of the other string.

      @return `*this`

      @param s The string to replace
      the contents with.

      @throw std::length_error `s.size() > max_size()`.
  */
  template<std::size_t M
#ifndef BOOST_STATIC_STRING_DOCS
  , typename std::enable_if<(M < N)>::type* = nullptr
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  assign(const basic_static_string<M, CharT, Traits>& s)
  {
    return assign_unchecked(s.data(), s.size());
  }

#ifndef BOOST_STATIC_STRING_DOCS
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  assign(const basic_static_string& s) noexcept
  {
    if (data() == s.data())
      return *this;
    return assign_unchecked(s.data(), s.size());
  }

  template<std::size_t M,
    typename std::enable_if<(M > N)>::type* = nullptr>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  assign(const basic_static_string<M, CharT, Traits>& s)
  {
    return assign(s.data(), s.size());
  }
#endif

  /** Assign to the string.

      Replaces the contents with those of the string `sub`,
      where `sub` is `s.substr(pos, count)`.

      @par Complexity

      Linear in `sub.size()`.

      @par Exception Safety

      Strong guarantee.

      @tparam M The capacity of the other string.

      @return `*this`

      @param s The string to replace
      the contents with.

      @param pos The index at which to begin the substring.

      @param count The size of the substring. The default
      argument for this parameter is @ref npos.

      @throw std::length_error `sub.size() > max_size()`.
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  assign(
    const basic_static_string<M, CharT, Traits>& s,
    size_type pos,
    size_type count = npos)
  {
    return assign(s.data() + pos, s.capped_length(pos, count));
  }

  /** Assign to the string.

      Replaces the contents with those of `{s, s + count)`.

      @par Complexity

      Linear in `count`.

      @par Exception Safety

      Strong guarantee.

      @note

      The range can contain null characters.

      @return `*this`

      @param count The number of characters to copy.

      @param s A pointer to the string to copy from.

      @throw std::length_error `count > max_size()`.
    */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  assign(
    const_pointer s,
    size_type count);

  /** Assign to the string.

      Replaces the contents with those of
      `{s, s + traits_type::length(s))`.

      @par Complexity

      Linear in `count`.

      @par Exception Safety

      Strong guarantee.

      @return `*this`

      @param s A pointer to the string to copy from.

      @throw std::length_error `traits_type::length(s) > max_size()`.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  assign(const_pointer s)
  {
    return assign(s, traits_type::length(s));
  }

  /** Assign to the string.

      Replaces the contents with the characters
      in the range `{first, last)`.

      @par Complexity

      Linear in `std::distance(first, last)`.

      @par Exception Safety

      Strong guarantee.

      @tparam InputIterator The type of the iterators.

      @par Constraints

      `InputIterator` satisfies __InputIterator__.

      @return `*this`

      @param first An iterator referring to the
      first character to assign.

      @param last An iterator past the end
      of the range to assign from.

      @throw std::length_error `std::distance(first, last) > max_size()`.
  */
  template<typename InputIterator>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
#ifdef BOOST_STATIC_STRING_DOCS
  basic_static_string&
#else
  typename std::enable_if<
    detail::is_input_iterator<InputIterator>::value,
      basic_static_string&>::type
#endif
  assign(
    InputIterator first,
    InputIterator last);

  /** Assign to the string.

      Replaces the contents with those of the
      initializer list `ilist`.

      @par Complexity

      Linear in `init.size()`.

      @par Exception Safety

      Strong guarantee.

      @return `*this`

      @param ilist The initializer list to copy from.

      @throw std::length_error `ilist.size() > max_size()`.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  assign(
    std::initializer_list<value_type> ilist)
  {
    return assign(ilist.begin(), ilist.end());
  }

  /** Assign to the string.

      Replaces the contents with those of
      `sv`, where `sv` is `string_view_type(t)`.

      @par Complexity

      Linear in `sv.size()`.

      @par Exception Safety

      Strong guarantee.

      @note

      The view can contain null characters.

      @tparam T A type convertible to `string_view_type`.

      @par Constraints

      @code
      std::is_convertible<const T&, string_view>::value &&
      !std::is_convertible<const T&, const CharT*>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value
      @endcode

      @return `*this`

      @param t The object to assign from.

      @throw std::length_error `sv.size() > max_size()`.
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  assign(const T& t)
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    return assign(sv.data(), sv.size());
  }

  /** Assign to the string.

      Replaces the contents with those of the substring `sv`,
      where `sv` is `string_view_type(t).substr(pos, count)`.

      @par Complexity

      Linear in `sv.size()`.

      @par Exception Safety

      Strong guarantee.

      @note

      The view can contain null characters.

      @tparam T A type convertible to `string_view_type`.

      @par Constraints

      @code
      std::is_convertible<const T&, string_view>::value &&
      !std::is_convertible<const T&, const CharT*>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value
      @endcode

      @return `*this`

      @param t The object to assign from.

      @param pos The index at which to begin the substring.

      @param count The size of the substring. The default
      argument for this parameter is @ref npos.

      @throw std::length_error `sv.size() > max_size()`.
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  basic_static_string&
  assign(
    const T& t,
    size_type pos,
    size_type count = npos)
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    if( pos > sv.size() )
      detail::throw_exception<std::out_of_range>(
          "pos >= t.size()");
    std::size_t rlen = (std::min)( count, sv.size() - pos );
    return assign(sv.data() + pos, rlen);
  }

  //--------------------------------------------------------------------------
  //
  // Element access
  //
  //--------------------------------------------------------------------------

  /** Access a character with bounds checking.

      Returns a reference to the character at
      index `pos`.

      @par Complexity

      Constant.

      @par Exception Safety

      Strong guarantee.

      @param pos The index to access.

      @throw std::out_of_range `pos >= size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  reference
  at(size_type pos)
  {
    if (pos >= size())
      detail::throw_exception<std::out_of_range>(
        "pos >= size()");
    return data()[pos];
  }

  /** Access a character with bounds checking.

      Returns a reference to the character at
      index `pos`.

      @par Complexity

      Constant.

      @par Exception Safety

      Strong guarantee.

      @param pos The index to access.

      @throw std::out_of_range `pos >= size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  const_reference
  at(size_type pos) const
  {
    if (pos >= size())
      detail::throw_exception<std::out_of_range>(
        "pos >= size()");
    return data()[pos];
  }

  /** Access a character.

      Returns a reference to the character at
      index `pos`.

      @par Complexity

      Constant.

      @par Precondition

      `pos >= size`

      @param pos The index to access.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  reference
  operator[](size_type pos) noexcept
  {
    return data()[pos];
  }

  /** Access a character.

      Returns a reference to the character at
      index `pos`.

      @par Complexity

      Constant.

      @par Precondition

      `pos >= size`

      @param pos The index to access.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  const_reference
  operator[](size_type pos) const noexcept
  {
    return data()[pos];
  }

  /** Return the first character.

      Returns a reference to the first character.

      @par Complexity

      Constant.

      @par Precondition

      `not empty()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  reference
  front() noexcept
  {
    return data()[0];
  }

  /** Return the first character.

      Returns a reference to the first character.

      @par Complexity

      Constant.

      @par Precondition

      `not empty()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  const_reference
  front() const noexcept
  {
    return data()[0];
  }

  /** Return the last character.

      Returns a reference to the last character.

      @par Complexity

      Constant.

      @par Precondition

      `not empty()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  reference
  back() noexcept
  {
    return data()[size() - 1];
  }

  /** Return the last character.

      Returns a reference to the last character.

      @par Complexity

      Constant.

      @par Precondition

      `not empty()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  const_reference
  back() const noexcept
  {
    return data()[size() - 1];
  }

  /** Return a pointer to the string.

      Returns a pointer to the underlying array
      serving as storage. The value returned is such that
      the range `{data(), data() + size())` is always a
      valid range, even if the container is empty.

      @par Complexity

      Constant.

      @note The value returned from this function
      is never never a null pointer value.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  pointer
  data() noexcept
  {
    return this->data_impl();
  }

  /** Return a pointer to the string.

      Returns a pointer to the underlying array
      serving as storage. The value returned is such that
      the range `{data(), data() + size())` is always a
      valid range, even if the container is empty.

      @par Complexity

      Constant.

      @note The value returned from this function
      is never never a null pointer value.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  const_pointer
  data() const noexcept
  {
    return this->data_impl();
  }

  /** Return a pointer to the string.

      Returns a pointer to the underlying array
      serving as storage. The value returned is such that
      the range `{c_str(), c_str() + size())` is always a
      valid range, even if the container is empty.

      @par Complexity

      Constant.

      @note The value returned from this function
      is never never a null pointer value.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  const_pointer
  c_str() const noexcept
  {
    return data();
  }

#ifdef BOOST_STATIC_STRING_DOCS
  /** Convert to a string view referring to the string.

      Returns a string view referring to the
      underlying character string.

      @par Complexity

      Constant.
  */
  BOOST_STATIC_STRING_CPP11_CONSTEXPR
  operator string_view_type() const noexcept
  {
    return string_view_type(data(), size());
  }
#else
#ifdef BOOST_STATIC_STRING_HAS_STD_STRING_VIEW
  BOOST_STATIC_STRING_CPP11_CONSTEXPR
  operator std::basic_string_view<CharT, Traits>() const noexcept
  {
    return std::basic_string_view<CharT, Traits>(data(), size());
  }
#endif
#ifndef BOOST_STATIC_STRING_STANDALONE
  BOOST_STATIC_STRING_CPP11_CONSTEXPR
  operator ::boost::basic_string_view<CharT, Traits>() const noexcept
  {
    return ::boost::basic_string_view<CharT, Traits>(data(), size());
  }

  BOOST_STATIC_STRING_CPP11_CONSTEXPR
  operator ::boost::core::basic_string_view<CharT>() const noexcept
  {
    return ::boost::core::basic_string_view<CharT>(data(), size());
  }
#endif
#endif

  //--------------------------------------------------------------------------
  //
  // Iterators
  //
  //--------------------------------------------------------------------------

  /// Return an iterator to the beginning.
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  iterator
  begin() noexcept
  {
    return data();
  }

  /// Return an iterator to the beginning.
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  const_iterator
  begin() const noexcept
  {
    return data();
  }

  /// Return an iterator to the beginning.
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  const_iterator
  cbegin() const noexcept
  {
    return data();
  }

  /// Return an iterator to the end.
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  iterator
  end() noexcept
  {
    return data() + size();
  }

  /// Return an iterator to the end.
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  const_iterator
  end() const noexcept
  {
    return data() + size();
  }

  /// Return an iterator to the end.
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  const_iterator
  cend() const noexcept
  {
    return data() + size();
  }

  /// Return a reverse iterator to the beginning.
  BOOST_STATIC_STRING_CPP17_CONSTEXPR
  reverse_iterator
  rbegin() noexcept
  {
    return reverse_iterator{end()};
  }

  /// Return a reverse iterator to the beginning.
  BOOST_STATIC_STRING_CPP17_CONSTEXPR
  const_reverse_iterator
  rbegin() const noexcept
  {
    return const_reverse_iterator{cend()};
  }

  /// Return a reverse iterator to the beginning.
  BOOST_STATIC_STRING_CPP17_CONSTEXPR
  const_reverse_iterator
  crbegin() const noexcept
  {
    return const_reverse_iterator{cend()};
  }

  /// Return a reverse iterator to the end.
  BOOST_STATIC_STRING_CPP17_CONSTEXPR
  reverse_iterator
  rend() noexcept
  {
    return reverse_iterator{begin()};
  }

  /// Return a reverse iterator to the end.
  BOOST_STATIC_STRING_CPP17_CONSTEXPR
  const_reverse_iterator
  rend() const noexcept
  {
    return const_reverse_iterator{cbegin()};
  }

  /// Return a reverse iterator to the end.
  BOOST_STATIC_STRING_CPP17_CONSTEXPR
  const_reverse_iterator
  crend() const noexcept
  {
    return const_reverse_iterator{cbegin()};
  }

  //--------------------------------------------------------------------------
  //
  // Capacity
  //
  //--------------------------------------------------------------------------

  /** Return if the string is empty.

      Returns whether the string contains no characters.

      @par Complexity

      Constant.

      @return `size() == 0`
  */
  BOOST_STATIC_STRING_NODISCARD
  BOOST_STATIC_STRING_CPP11_CONSTEXPR
  bool
  empty() const noexcept
  {
    return size() == 0;
  }

  /** Return the size of the string.

      Returns the number of characters stored in the
      string, excluding the null terminator.

      @par Complexity

      Constant.
  */
  BOOST_STATIC_STRING_CPP11_CONSTEXPR
  size_type
  size() const noexcept
  {
    return this->size_impl();
  }

  /** Return the size of the string.

      Returns the number of characters stored in the
      string, excluding the null terminator.

      @par Complexity

      Constant.
  */
  BOOST_STATIC_STRING_CPP11_CONSTEXPR
  size_type
  length() const noexcept
  {
    return size();
  }

  /** Return the number of characters that can be stored.

      Returns the maximum size of the string, excluding the
      null terminator. The returned value is always `N`.

      @par Complexity

      Constant.
   */
  BOOST_STATIC_STRING_CPP11_CONSTEXPR
  size_type
  max_size() const noexcept
  {
    return N;
  }

  /** Increase the capacity.

      This function has no effect.

      @throw std::length_error `n > max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  void
  reserve(size_type n)
  {
    if (n > max_size())
      detail::throw_exception<std::length_error>(
        "n > max_size()");
  }

  /** Return the number of characters that can be stored.

      Returns the maximum size of the string, excluding the
      null terminator. The returned value is always `N`.

      @par Complexity

      Constant.
   */
  BOOST_STATIC_STRING_CPP11_CONSTEXPR
  size_type
  capacity() const noexcept
  {
    return max_size();
  }

  /** Request the removal of unused capacity.

      This function has no effect.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  void
  shrink_to_fit() noexcept { }

  //--------------------------------------------------------------------------
  //
  // Operations
  //
  //--------------------------------------------------------------------------

  /** Clear the contents.

      Erases all characters from the string. After this
      call, @ref size() returns zero.

      @par Complexity

      Linear in @ref size().

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  void
  clear() noexcept
  {
    this->set_size(0);
    term();
  }

  /** Insert into the string.

      Inserts `count` copies of `ch` at the position `index`.

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return `*this`

      @param index The index to insert at.
      @param count The number of characters to insert.
      @param ch The character to insert.

      @throw std::length_error `size() + count > max_size()`
      @throw std::out_of_range `index > size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  insert(
    size_type index,
    size_type count,
    value_type ch)
  {
    if (index > size())
      detail::throw_exception<std::out_of_range>(
        "index > size()");
    insert(begin() + index, count, ch);
    return *this;
  }

  /** Insert into the string.

      Inserts the null-terminated character string pointed to by `s`
      of length `count` at the position `index` where `count`
      is `traits_type::length(s)`.

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return `*this`

      @param index The index to insert at.
      @param s The string to insert.

      @throw std::length_error `size() + count > max_size()`
      @throw std::out_of_range `index > size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  insert(
    size_type index,
    const_pointer s)
  {
    return insert(index, s, traits_type::length(s));
  }

  /** Insert into the string.

      Inserts `count` characters of the string pointed to by `s`
      at the position `index`.

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return `*this`

      @param index The index to insert at.
      @param s The string to insert.
      @param count The length of the string to insert.

      @throw std::length_error `size() + count > max_size()`
      @throw std::out_of_range `index > size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  insert(
    size_type index,
    const_pointer s,
    size_type count)
  {
    if (index > size())
      detail::throw_exception<std::out_of_range>(
        "index > size()");
    insert(data() + index, s, s + count);
    return *this;
  }

  /** Insert into the string.

      Inserts the string `str`
      at the position `index`.

      @par Exception Safety

      Strong guarantee.

      @note The insertion is done unchecked when
      the capacity of `str` differs from that of the
      string the function is called on.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @tparam M The size of the input string.

      @return `*this`

      @param index The index to insert at.
      @param str The string to insert.

      @throw std::length_error `size() + str.size() > max_size()`
      @throw std::out_of_range `index > size()`
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  insert(
    size_type index,
    const basic_static_string<M, CharT, Traits>& str)
  {
    return insert_unchecked(index, str.data(), str.size());
  }

#ifndef BOOST_STATIC_STRING_DOCS
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  insert(
    size_type index,
    const basic_static_string& str)
  {
    return insert(index, str.data(), str.size());
  }
#endif

  /** Insert into the string.

      Inserts a string, obtained by `str.substr(index_str, count)`
      at the position `index`.

      @par Exception Safety

      Strong guarantee.

      @note The insertion is done unchecked when
      the capacity of `str` differs from that of the
      string the function is called on.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @tparam M The size of the input string.

      @return `*this`

      @param index The index to insert at.
      @param str The string from which to insert.
      @param index_str The index in `str` to start inserting from.
      @param count The number of characters to insert.
      The default argument for this parameter is @ref npos.

      @throw std::length_error `size() + str.substr(index_str, count).size() > max_size()`
      @throw std::out_of_range `index > size()`
      @throw std::out_of_range `index_str > str.size()`
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  insert(
    size_type index,
    const basic_static_string<M, CharT, Traits>& str,
    size_type index_str,
    size_type count = npos)
  {
    return insert_unchecked(index, str.data() + index_str, str.capped_length(index_str, count));
  }

#ifndef BOOST_STATIC_STRING_DOCS
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  insert(
    size_type index,
    const basic_static_string& str,
    size_type index_str,
    size_type count = npos)
  {
    return insert(index, str.data() + index_str, str.capped_length(index_str, count));
  }
#endif

  /** Insert into the string.

      Inserts the character `ch` before the character pointed by `pos`.

      @par Precondition

      `pos` shall be vaild within `{data(), data() + size()}`

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return An iterator which refers to the first inserted character
      or `pos` if no characters were inserted

      @param pos The index to insert at.
      @param ch The character to insert.

      @throw std::length_error `size() + 1 > max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  iterator
  insert(
    const_iterator pos,
    value_type ch)
  {
    return insert(pos, 1, ch);
  }

  /** Insert into the string.

      Inserts `count` copies of `ch` before the character pointed by `pos`.

      @par Precondition

      `pos` shall be valid within `{data(), data() + size()}`

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return An iterator which refers to the first inserted character
      or `pos` if no characters were inserted

      @param pos The position to insert at.
      @param count The number of characters to insert.
      @param ch The character to insert.

      @throw std::length_error `size() + count > max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  iterator
  insert(
    const_iterator pos,
    size_type count,
    value_type ch);

  /** Insert into the string.

      Inserts characters from the range `{first, last)` before the
      character pointed to by `pos`.

      @par Precondition

      `pos` shall be valid within `{data(), data() + size()}`,

      `{first, last)` shall be a valid range

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @tparam InputIterator The type of the iterators.

      @par Constraints

      `InputIterator` satisfies __InputIterator__ and does not
      satisfy __ForwardIterator__.

      @return An iterator which refers to the first inserted character
      or `pos` if no characters were inserted

      @param pos The position to insert at.
      @param first An iterator representing the first character to insert.
      @param last An iterator representing one past the last character to insert.

      @throw std::length_error `size() + insert_count > max_size()`
  */
  template<typename InputIterator>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
#ifdef BOOST_STATIC_STRING_DOCS
  iterator
#else
  typename std::enable_if<
    detail::is_input_iterator<
      InputIterator>::value &&
        !detail::is_forward_iterator<
          InputIterator>::value, iterator>::type
#endif
  insert(
    const_iterator pos,
    InputIterator first,
    InputIterator last);

#ifndef BOOST_STATIC_STRING_DOCS
  template<typename ForwardIterator>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  typename std::enable_if<
    detail::is_forward_iterator<
      ForwardIterator>::value,
        iterator>::type
  insert(
    const_iterator pos,
    ForwardIterator first,
    ForwardIterator last);
#endif

  /** Insert into the string.

      Inserts characters from `ilist` before `pos`.

      @par Precondition

      `pos` shall be valid within `{data(), data() + size()}`

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return An iterator which refers to the first inserted character
      or `pos` if no characters were inserted

      @param pos The position to insert at.
      @param ilist The initializer list from which to insert.

      @throw std::length_error `size() + ilist.size() > max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  iterator
  insert(
    const_iterator pos,
    std::initializer_list<value_type> ilist)
  {
    return insert_unchecked(pos, ilist.begin(), ilist.size());
  }

  /** Insert into the string.

      Constructs a temporary `string_view_type` object `sv` from `t` and
      inserts `{sv.begin(), sv.end())` at `index`.

      @par Precondition

      `index` shall be valid within `{data(), data() + size()}`

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return `*this`

      @tparam T The type of the object to convert.

      @par Constraints

      `std::is_convertible<const T&, string_view>::value &&
      !std::is_convertible<const T&, const CharT*>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value`.

      @param index The index to insert at.
      @param t The string to insert from.

      @throw std::length_error `size() + sv.size() > max_size()`
      @throw std::out_of_range `index > size()`
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  insert(
    size_type index,
    const T& t)
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    return insert(index, sv.data(), sv.size());
  }

  /** Insert into the string.

      Constructs a temporary `string_view_type` object `sv` from `t`
      and inserts `sv.substr(index_str, count)` at `index`.

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @tparam T The type of the object to convert.

      @par Constraints

      `std::is_convertible<const T&, string_view>::value &&
      !std::is_convertible<const T&, const_pointer>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value`.

      @return `*this`

      @param index The index to insert at.
      @param t The string to insert from.
      @param index_str The index in the temporary `string_view_type` object
      to start the substring from.
      @param count The number of characters to insert.

      @throw std::length_error `size() + sv.size() > max_size()`
      @throw std::out_of_range `index > size()`
      @throw std::out_of_range `index_str > sv.size()`
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  insert(
    size_type index,
    const T& t,
    size_type index_str,
    size_type count = npos)
  {
    detail::common_string_view_type<T, CharT, Traits> sv(t);
    if ( index_str > sv.size() )
      detail::throw_exception<std::out_of_range>("index_str > t.size()");
    return insert(index, sv.data() + index_str, (std::min)(sv.size() - index_str, count));
  }

  /** Erase from the string.

      Erases `num` characters from the string, starting at `index`.
      `num` is determined as the smaller of `count` and `size() - index`.

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return `*this`

      @param index The index to erase at.
      The default argument for this parameter is `0`.

      @param count The number of characters to erase.
      The default argument for this parameter is @ref npos.

      @throw std::out_of_range `index > size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  erase(
    size_type index = 0,
    size_type count = npos)
  {
    erase(data() + index, data() + index + capped_length(index, count));
    return *this;
  }

  /** Erase from the string.

      Erases the character at `pos`.

      @par Preconditions

      `pos` shall be valid within `{data(), data() + size()}`

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return An iterator referring to character immediately following
      the erased character, or @ref end() if one does not exist.

      @param pos An iterator referring to the character to erase.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  iterator
  erase(const_iterator pos)
  {
    BOOST_STATIC_STRING_ASSERT(!empty());
    return erase(pos, pos + 1);
  }

  /** Erase from the string.

      Erases the characters in the range `{first, last)`.

      @par Precondition

      `{first, last}` shall be valid within `{data(), data() + size()}`

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return An iterator referring to the character `last`
      previously referred to, or @ref end() if one does not exist.

      @param first An iterator referring to the first character to erase.

      @param last An iterator past the last character to erase.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  iterator
  erase(
    const_iterator first,
    const_iterator last);

  /** Append a character.

      Appends a character to the end of the string.

      @par Exception Safety

      Strong guarantee.

      @param ch The character to append.

      @throw std::length_error `size() >= max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  void
  push_back(value_type ch);

  /** Remove the last character.

      Removes a character from the end of the string.

      @par Precondition

      `not empty()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  void
  pop_back() noexcept
  {
    BOOST_STATIC_STRING_ASSERT(!empty());
    this->set_size(size() - 1);
    term();
  }

  /** Append to the string.

      Appends `count` copies of `ch` to the end of the string.

      @par Exception Safety

      Strong guarantee.

      @return `*this`

      @param count The number of characters to append.

      @param ch The character to append.

      @throw std::length_error `size() + count > max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  append(
    size_type count,
    value_type ch);

  /** Append to the string.

      Appends `s` to the end of the string.

      @par Exception Safety

      Strong guarantee.

      @tparam M The size of the string to append.

      @return `*this`

      @param s The string to append.

      @throw std::length_error `size() + s.size() > max_size()`
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  append(
    const basic_static_string<M, CharT, Traits>& s)
  {
    return append(s.data(), s.size());
  }

  /** Append to the string.

      Appends the substring `sub` to the end of the string,
      where `sub` is `s.substr(pos, count)`.

      @par Exception Safety

      Strong guarantee.

      @tparam M The size of the string to append.

      @return `*this`

      @param s The string to append.

      @param pos The index at which to begin the substring.

      @param count The size of the substring. The default
      argument for this parameter is @ref npos.

      @throw std::length_error `size() + sub.size() > max_size()`
      @throw std::out_of_range `pos > s.size()`
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  append(
    const basic_static_string<M, CharT, Traits>& s,
    size_type pos,
    size_type count = npos)
  {
    return append(s.data() + pos, s.capped_length(pos, count));
  }

  /** Append to the string.

      Appends `count` characters from the string pointed
      to by `s` to the end of the string.

      @par Exception Safety

      Strong guarantee.

      @note The string can contain null characters.

      @return `*this`

      @param s The string to append.

      @param count The number of characters to append.

      @throw std::length_error `size() + count > max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  append(
    const_pointer s,
    size_type count);

  /** Append to the string.

      Appends `count` characters from the string pointed
      to by `s` to the end of the string, where `count`
      is `traits_type::length(s)`.

      @par Exception Safety

      Strong guarantee.

      @return `*this`

      @param s The string to append.

      @throw std::length_error `size() + count > max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  append(const_pointer s)
  {
    return append(s, traits_type::length(s));
  }

  // KRYSTIAN TODO: change exception safety
  /** Append to the string.

      Appends characters from the range `{first, last)`
      to the end of the string.

      @par Precondition

      `{first, last)` shall be a valid range

      @par Exception Safety

      Strong guarantee.

      @tparam InputIterator The type of the iterators.

      @par Constraints

      `InputIterator` satisfies __InputIterator__.

      @return `*this`

      @param first An iterator referring to the
      first character to append.

      @param last An iterator past the end of
      last character to append.

      @throw std::length_error `size() + std::distance(first, last) > max_size()`
  */
  template<typename InputIterator>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
#ifdef BOOST_STATIC_STRING_DOCS
  basic_static_string&
#else
  typename std::enable_if<
    detail::is_input_iterator<InputIterator>::value,
      basic_static_string&>::type
#endif
  append(
    InputIterator first,
    InputIterator last)
  {
    this->set_size(size() + read_back(true, first, last));
    return term();
  }

  /** Append to the string.

      Appends the characters from `ilist` to the
      end of the string.

      @par Exception Safety

      Strong guarantee.

      @return `*this`

      @param ilist The initializer list to append.

      @throw std::length_error `size() + ilist.size() > max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  append(
    std::initializer_list<value_type> ilist)
  {
    return append(ilist.begin(), ilist.size());
  }

  /** Append to the string.

      Appends `sv` to the end of the string,
      where `sv` is `string_view_type(t)`.

      @par Exception Safety

      Strong guarantee.

      @tparam T The type of the object to convert.

      @par Constraints

      @code
      std::is_convertible<T const&, string_view>::value &&
      !std::is_convertible<T const&, char const*>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value
      @endcode

      @return `*this`

      @param t The string to append.

      @throw std::length_error `size() + sv.size() > max_size()`
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  append(const T& t)
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    return append(sv.data(), sv.size());
  }

  /** Append to the string.

      Appends the substring `sv` to the end of the string,
      where `sv` is `string_view_type(t).substr(pos, count)`.

      @par Exception Safety

      Strong guarantee.

      @tparam T The type of the object to convert.

      @par Constraints

      @code
      std::is_convertible<T const&, string_view>::value &&
      !std::is_convertible<T const&, char const*>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value
      @endcode

      @return `*this`

      @param t The object to append.

      @param pos The index at which to begin the substring.

      @param count The size of the substring. The default
      argument for this parameter is @ref npos.

      @throw std::length_error `size() + sv.size() > max_size()`
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  append(
    const T& t,
    size_type pos,
    size_type count = npos)
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    if ( pos > sv.size() )
      detail::throw_exception<std::out_of_range>("pos > t.size()");
    return append(sv.data() + pos, (std::min)(sv.size() - pos, count));
  }

  /** Append to the string.

      Appends `s` to the end of the string.

      @par Exception Safety

      Strong guarantee.

      @tparam M The size of the string to append.

      @return `*this`

      @param s The string to append.

      @throw std::length_error `size() + s.size() > max_size()`
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  operator+=(
    const basic_static_string<M, CharT, Traits>& s)
  {
    return append(s);
  }

  /** Append to the string.

      Appends a character to the end of the string.

      @par Exception Safety

      Strong guarantee.

      @param ch The character to append.

      @throw std::length_error `size() >= max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  operator+=(value_type ch)
  {
    push_back(ch);
    return *this;
  }

  /** Append to the string.

      Appends `count` characters from the string pointed
      to by `s` to the end of the string, where `count`
      is `traits_type::length(s)`.

      @par Exception Safety

      Strong guarantee.

      @return `*this`

      @param s The string to append.

      @throw std::length_error `size() + count > max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  operator+=(const_pointer s)
  {
    return append(s);
  }

  /** Append to the string.

      Appends the characters from `ilist` to the
      end of the string.

      @par Exception Safety

      Strong guarantee.

      @return `*this`

      @param ilist The initializer list to append.

      @throw std::length_error `size() + ilist.size() > max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  operator+=(
    std::initializer_list<value_type> ilist)
  {
    return append(ilist);
  }

  /** Append to the string.

      Appends `sv` to the end of the string,
      where `sv` is `string_view_type(t)`.

      @par Exception Safety

      Strong guarantee.

      @tparam T The type of the object to convert.

      @par Constraints

      @code
      std::is_convertible<T const&, string_view>::value &&
      !std::is_convertible<T const&, char const*>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value
      @endcode

      @return `*this`

      @param t The string to append.

      @throw std::length_error `size() + sv.size() > max_size()`
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  operator+=(const T& t)
  {
    return append(t);
  }

  /** Compare a string with the string.

      Let `comp` be `traits_type::compare(data(), s.data(), std::min(size(), s.size())`.
      If `comp != 0`, then the result is `comp`. Otherwise, the result is
      `0` if `size() == s.size()`, `-1` if `size() < s.size()`, and `1`
      otherwise.

      @par Complexity
      Linear.

      @return The result of lexicographically comparing `s` and the string.

      @tparam M The size of the string to compare with.

      @param s The string to compare.
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  int
  compare(
    const basic_static_string<M, CharT, Traits>& s) const noexcept
  {
    return detail::lexicographical_compare<CharT, Traits>(
      data(), size(), s.data(), s.size());
  }

  /** Compare a string with the string.

    Let `sub` be `substr(pos1, count1)` and `comp` be
    `traits_type::compare(sub.data(), s.data(), std::min(sub.size(), s.size())`.
    If `comp != 0`, then the result is `comp`. Otherwise, the result is
    `0` if `sub.size() == s.size()`, `-1` if `sub.size() < s.size()`, and `1`
    otherwise.

    @par Complexity

    Linear.

    @par Exception Safety

    Strong guarantee.

    @return The result of lexicographically comparing `sub` and `s`.

    @tparam M The size of the string to compare with.

    @param pos1 The index at which to begin the substring.

    @param count1 The size of the substring.

    @param s The string to compare.

    @throw std::out_of_range `pos1 > size()`
    */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  int
  compare(
    size_type pos1,
    size_type count1,
    const basic_static_string<M, CharT, Traits>& s) const
  {
    return detail::lexicographical_compare<CharT, Traits>(
      data() + pos1, capped_length(pos1, count1), s.data(), s.size());
  }

  /** Compare a string with the string.

      Let `sub1` be `substr(pos1, count1)`, `sub2` be
      `s.substr(pos2, count2)`, and `comp` be
      `traits_type::compare(sub1.data(), sub2.data(), std::min(sub1.size(), sub2.size())`.
      If `comp != 0`, then the result is `comp`. Otherwise, the result is
      `0` if `sub1.size() == sub2.size()`, `-1` if `sub1.size() < sub2.size()`, and `1`
      otherwise.

      @par Complexity

      Linear.

      @par Exception Safety

      Strong guarantee.

      @return The result of lexicographically comparing `sub1` and `sub2`.

      @param pos1 The index at which to begin the substring.

      @param count1 The size of the substring.

      @param s The string to compare.

      @param pos2 The index at which to begin the substring to compare.

      @param count2 The size of the substring to compare.

      @throw std::out_of_range `pos1 > size()`

      @throw std::out_of_range `pos2 > s.size()`
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  int
  compare(
    size_type pos1,
    size_type count1,
    const basic_static_string<M, CharT, Traits>& s,
    size_type pos2,
    size_type count2 = npos) const
  {
    return detail::lexicographical_compare<CharT, Traits>(
      data() + pos1, capped_length(pos1, count1),
      s.data() + pos2, s.capped_length(pos2, count2));
  }

  /** Compare a string with the string.

      Let `len` be `traits_type::length(s)` and `comp` be
      `traits_type::compare(data(), s, std::min(size(), len)`.
      If `comp != 0`, then the result is `comp`. Otherwise, the result is
      `0` if `size() == len`, `-1` if `size() < len`, and `1`
      otherwise.

      @par Complexity

      Linear.

      @return The result of lexicographically comparing `s` and the string.

      @param s The string to compare.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  int
  compare(const_pointer s) const noexcept
  {
    return detail::lexicographical_compare<CharT, Traits>(
      data(), size(), s, traits_type::length(s));
  }

  /** Compare a string with the string.

      Let `sub` be `substr(pos1, count1)`, `len` be
      `traits_type::length(s)`, and `comp` be
      `traits_type::compare(sub.data(), s, std::min(size(), len)`.
      If `comp != 0`, then the result is `comp`. Otherwise, the result is
      `0` if `sub.size() == len`, `-1` if `sub.size() < len`, and `1`
      otherwise.

      @par Complexity

      Linear.

      @par Exception Safety

      Strong guarantee.

      @return The result of lexicographically comparing `s` and `sub`.

      @param pos1 The index at which to begin the substring.

      @param count1 The size of the substring.

      @param s The string to compare.

      @throw std::out_of_range `pos1 > size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  int
  compare(
    size_type pos1,
    size_type count1,
    const_pointer s) const
  {
    return detail::lexicographical_compare<CharT, Traits>(
      data() + pos1, capped_length(pos1, count1), s, traits_type::length(s));
  }

  /** Compare a string with the string.

      Let `sub` be `substr(pos1, count1)`, and `comp` be
      `traits_type::compare(sub.data(), s, std::min(size(), count2)`.
      If `comp != 0`, then the result is `comp`. Otherwise, the result is
      `0` if `sub.size() == count2`, `-1` if `sub.size() < count2`, and `1`
      otherwise.

      @par Complexity

      Linear.

      @par Exception Safety

      Strong guarantee.

      @return The result of lexicographically comparing `s` and `sub`.

      @param pos1 The index at which to begin the substring.

      @param count1 The size of the substring.

      @param s The string to compare.

      @param count2 The length of the string to compare.

      @throw std::out_of_range `pos1 > size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  int
  compare(
    size_type pos1,
    size_type count1,
    const_pointer s,
    size_type count2) const
  {
    return detail::lexicographical_compare<CharT, Traits>(
      data() + pos1, capped_length(pos1, count1), s, count2);
  }

  /** Compare a string with the string.

      Let `s` be `string_view_type(t)` and `comp` be
      `traits_type::compare(data(), s.data(), std::min(size(), s.size())`.
      If `comp != 0`, then the result is `comp`. Otherwise, the result is
      `0` if `size() == s.size()`, `-1` if `size() < s.size()`, and `1`
      otherwise.

      @par Complexity

      Linear.

      @par Exception Safety

      Strong guarantee.

      @tparam T The type of the object to convert.

      @par Constraints

      @code
      std::is_convertible<const T&, string_view>::value &&
      !std::is_convertible<const T&, const_pointer>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value.
      @endcode

      @return The result of lexicographically comparing `s` and the string.

      @param t The string to compare.
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  int
  compare(const T& t) const noexcept
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    return detail::lexicographical_compare<CharT, Traits>(
      data(), size(), sv.data(), sv.size());
  }

  /** Compare a string with the string.

      Let `s` be `string_view_type(t)`, `sub` be
      `substr(pos1, count1)`, and `comp` be
      `traits_type::compare(sub.data(), s.data(), std::min(sub.size(), s.size())`.
      If `comp != 0`, then the result is `comp`. Otherwise, the result is
      `0` if `sub.size() == s.size()`, `-1` if `sub.size() < s.size()`, and `1`
      otherwise.

      @par Complexity

      Linear.

      @par Exception Safety

      Strong guarantee.

      @tparam T The type of the object to convert.

      @par Constraints

      @code
      std::is_convertible<const T&, string_view>::value &&
      !std::is_convertible<const T&, const_pointer>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value.
      @endcode

      @return The result of lexicographically comparing `s` and `sub`.

      @param pos1 The index at which to begin the substring.

      @param count1 The length of the substring.

      @param t The string to compare.
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  int
  compare(
    size_type pos1,
    size_type count1,
    const T& t) const
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    return detail::lexicographical_compare<CharT, Traits>(
      data() + pos1, capped_length(pos1, count1), sv.data(), sv.size());
  }

  /** Compare a string with the string.

      Let `sub1` be `substr(pos1, count1)`, `sub2` be
      `string_view_type(t).substr(pos2, count2)`, and `comp` be
      `traits_type::compare(sub1.data(), sub2.data(), std::min(sub1.size(), sub2.size())`.
      If `comp != 0`, then the result is `comp`. Otherwise, the result is
      `0` if `sub1.size() == sub2.size()`, `-1` if `sub1.size() < sub2.size()`, and `1`
      otherwise.

      @par Complexity

      Linear.

      @par Exception Safety

      Strong guarantee.

      @tparam T The type of the object to convert.

      @par Constraints

      @code
      std::is_convertible<const T&, string_view>::value &&
      !std::is_convertible<const T&, const_pointer>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value.
      @endcode

      @return The result of lexicographically comparing `sub1` and `sub2`.

      @param pos1 The index at which to begin the substring in the string.

      @param count1 The length of the substring in the string.

      @param t The string to compare.

      @param pos2 The index at which to begin the substring in the string view.

      @param count2 The length of the substring in the string view.
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  int
  compare(
    size_type pos1,
    size_type count1,
    const T& t,
    size_type pos2,
    size_type count2 = npos) const
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    if ( pos2 > sv.size())
      detail::throw_exception<std::out_of_range>("pos2 > sv.size()");
    return compare(
        pos1, count1, sv.data() + pos2,
        (std::min)(sv.size() - pos2, count2));
  }

  /** Return a substring.

      Returns a substring of the string.

      @par Exception Safety

      Strong guarantee.

      @return A string object containing the characters
      `{data() + pos, std::min(count, size() - pos))`.

      @param pos The index to being the substring at. The
      default argument for this parameter is `0`.
      @param count The length of the substring. The default argument
      for this parameter is @ref npos.

      @throw std::out_of_range `pos > size()`
  */
#ifndef BOOST_STATIC_STRING_GCC5_BAD_CONSTEXPR
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
#endif
  basic_static_string
  substr(
    size_type pos = 0,
    size_type count = npos) const
  {
    return basic_static_string(
      data() + pos, capped_length(pos, count));
  }

#ifdef BOOST_STATIC_STRING_HAS_ANY_STRING_VIEW
  /** Return a string view of a substring.

      Returns a view of a substring.

      @par Exception Safety

      Strong guarantee.

      @return A `string_view_type` object referring
      to `{data() + pos, std::min(count, size() - pos))`.

      @param pos The index to being the substring at. The
      default argument for this parameter is `0`.
      @param count The length of the substring. The default argument
      for this parameter is @ref npos.

      @throw std::out_of_range `pos > size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  string_view_type
  subview(
    size_type pos = 0,
    size_type count = npos) const
  {
    return string_view_type(
      data() + pos, capped_length(pos, count));
  }
#endif

  /** Copy a substring to another string.

      Copies `std::min(count, size() - pos)` characters starting at
      index `pos` to the string pointed to by `dest`.

      @note The resulting string is not null terminated.

      @return The number of characters copied.

      @param count The number of characters to copy.
      @param dest The string to copy to.
      @param pos The index to begin copying from. The
      default argument for this parameter is `0`.

      @throw std::out_of_range `pos > max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  copy(
    pointer dest,
    size_type count,
    size_type pos = 0) const
  {
    const auto num_copied = capped_length(pos, count);
    traits_type::copy(dest, data() + pos, num_copied);
    return num_copied;
  }

  /** Change the size of the string.

      Resizes the string to contain `n` characters. If
      `n > size()`, characters with the value `CharT()` are
      appended. Otherwise, `size()` is reduced to `n`.

      @param n The size to resize the string to.

      @throw std::out_of_range `n > max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  void
  resize(size_type n)
  {
    resize(n, value_type());
  }

  /** Change the size of the string.

      Resizes the string to contain `n` characters. If
      `n > size()`, copies of `c` are
      appended. Otherwise, `size()` is reduced to `n`.

      @param n The size to resize the string to.
      @param c The characters to append if the size
      increases.

      @throw std::out_of_range `n > max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  void
  resize(
    size_type n,
    value_type c);

  /**
      Resize the string and overwrite its contents.

      Resizes the string to contain `n` characters, and uses the
      provided function object `op` to overwrite the string contents.
      The function object is called with two arguments: a pointer to
      the string internal buffer, and the size of the string. The
      function object shall return the number of characters written to
      the buffer, which shall be less than or equal to `n`. The string
      size is set to the value returned by the function object.

      @par Exception Safety

      Strong guarantee. However, if an exception is thrown by
      `std::move(op)(p, count)`, the behavior is undefined.

      @throw std::length_error `n > max_size()`
  */
  template<typename Operation>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  void
  resize_and_overwrite(
    size_type n,
    Operation op);

  /** Swap two strings.

      Swaps the contents of the string and `s`.

      @par Exception Safety

      Strong guarantee.

      @note

      All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @param s The string to swap with.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  void
  swap(basic_static_string& s) noexcept;

  /** Swap two strings.

      Swaps the contents of the string and `s`.

      @par Exception Safety

      Strong guarantee.

      @note

      All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @tparam M The size of the string to swap with.

      @param s The string to swap with.

      @throw std::length_error `s.size() > max_size() || size() > s.max_size()`
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  void
  swap(basic_static_string<M, CharT, Traits>& s);

  /** Replace a part of the string.

      Replaces `rcount` characters starting at index `pos1` with those
      of `str`, where `rcount` is `std::min(n1, size() - pos1)`.

      @par Exception Safety

      Strong guarantee.

      @note The replacement is done unchecked when
      the capacity of `str` differs from that of the
      string the function is called on.

      All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @tparam M The size of the input string.

      @return `*this`

      @param pos1 The index to replace at.
      @param n1 The number of characters to replace.
      @param str The string to replace with.

      @throw std::length_error `size() + (str.size() - rcount) > max_size()`
      @throw std::out_of_range `pos1 > size()`
  */
  template<size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace(
    size_type pos1,
    size_type n1,
    const basic_static_string<M, CharT, Traits>& str)
  {
    return replace_unchecked(pos1, n1, str.data(), str.size());
  }

#ifndef BOOST_STATIC_STRING_DOCS
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace(
    size_type pos1,
    size_type n1,
    const basic_static_string& str)
  {
    return replace(pos1, n1, str.data(), str.size());
  }
#endif

  /** Replace a part of the string.

      Replaces `rcount` characters starting at index `pos1` with those of
      `str.subview(pos2, n2)`, where `rcount` is `std::min(n1, size() - pos1)`.

      @par Exception Safety

      Strong guarantee.

      @note The replacement is done unchecked when
      the capacity of `str` differs from that of the
      string the function is called on.

      All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return `*this`

      @param pos1 The index to replace at.
      @param n1 The number of characters to replace.
      @param str The string to replace with.
      @param pos2 The index to begin the substring.
      @param n2 The length of the substring.
      The default argument for this parameter is @ref npos.

      @throw std::length_error `size() + (std::min(str.size(), n2) - rcount) > max_size()`
      @throw std::out_of_range `pos1 > size()`
      @throw std::out_of_range `pos2 > str.size()`
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace(
    size_type pos1,
    size_type n1,
    const basic_static_string<M, CharT, Traits>& str,
    size_type pos2,
    size_type n2 = npos)
  {
    return replace_unchecked(pos1, n1, str.data() + pos2, str.capped_length(pos2, n2));
  }

#ifndef BOOST_STATIC_STRING_DOCS
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace(
    size_type pos1,
    size_type n1,
    const basic_static_string& str,
    size_type pos2,
    size_type n2 = npos)
  {
    return replace(pos1, n1, str.data() + pos2, str.capped_length(pos2, n2));
  }
#endif

  /** Replace a part of the string.

      Constructs a temporary `string_view_type` object `sv` from `t`, and
      replaces `rcount` characters starting at index `pos1` with those
      of `sv`, where `rcount` is `std::min(n1, size() - pos1)`.

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @tparam T The type of the object to convert.

      @par Constraints

      `std::is_convertible<const T&, string_view>::value &&
      !std::is_convertible<const T&, const CharT*>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value`.

      @return `*this`

      @param pos1 The index to replace at.
      @param n1 The number of characters to replace.
      @param t The object to replace with.

      @throw std::length_error `size() + (sv.size() - rcount) > max_size()`
      @throw std::out_of_range `pos1 > size()`
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace(
    size_type pos1,
    size_type n1,
    const T& t)
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    return replace(pos1, n1, sv.data(), sv.size());
  }

  /** Replace a part of the string.

      Constructs a temporary `string_view_type` object `sv` from `t`, and
      replaces `rcount` characters starting at index `pos1` with those
      of `sv.substr(pos2, n2)`, where `rcount` is `std::min(n1, size() - pos)`.

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @tparam T The type of the object to convert.

      @par Constraints

      `std::is_convertible<const T&, string_view>::value &&
      !std::is_convertible<const T&, const CharT*>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value`.

      @return `*this`

      @param pos1 The index to replace at.
      @param n1 The number of characters to replace.
      @param t The object to replace with.
      @param pos2 The index to begin the substring.
      @param n2 The length of the substring.
      The default argument for this parameter is @ref npos.

      @throw std::length_error `size() + (std::min(n2, sv.size()) - rcount) > max_size()`
      @throw std::out_of_range `pos1 > size()`
      @throw std::out_of_range `pos2 > sv.size()`
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace(
    size_type pos1,
    size_type n1,
    const T& t,
    size_type pos2,
    size_type n2 = npos)
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    if ( pos2 > sv.size())
      detail::throw_exception<std::out_of_range>("pos2 > t.size()");
    return replace(
        pos1, n1, sv.data() + pos2,
        (std::min)(sv.size() - pos2, n2));
  }

  /** Replace a part of the string.

      Replaces `rcount` characters starting at index `pos` with those of
      `{s, s + n2)`, where `rcount` is `std::min(n1, size() - pos)`.

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return `*this`

      @param pos The index to replace at.
      @param n1 The number of characters to replace.
      @param s The string to replace with.
      @param n2 The length of the string to replace with.

      @throw std::length_error `size() + (n2 - rcount) > max_size()`
      @throw std::out_of_range `pos > size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace(
    size_type pos,
    size_type n1,
    const_pointer s,
    size_type n2)
  {
    return replace(data() + pos, data() + pos + capped_length(pos, n1), s, n2);
  }

  /** Replace a part of the string.

      Replaces `rcount` characters starting at index `pos` with those of
      `{s, s + len)`, where the length of the string `len` is `traits_type::length(s)` and `rcount`
      is `std::min(n1, size() - pos)`.

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return `*this`

      @param pos The index to replace at.
      @param n1 The number of characters to replace.
      @param s The string to replace with.

      @throw std::length_error `size() + (len - rcount) > max_size()`
      @throw std::out_of_range `pos > size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace(
    size_type pos,
    size_type n1,
    const_pointer s)
  {
    return replace(pos, n1, s, traits_type::length(s));
  }

  /** Replace a part of the string.

      Replaces `rcount` characters starting at index `pos` with `n2` copies
      of `c`, where `rcount` is `std::min(n1, size() - pos)`.

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return `*this`

      @param pos The index to replace at.
      @param n1 The number of characters to replace.
      @param n2 The number of characters to replace with.
      @param c The character to replace with.

      @throw std::length_error `size() + (n2 - rcount) > max_size()`
      @throw std::out_of_range `pos > size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace(
    size_type pos,
    size_type n1,
    size_type n2,
    value_type c)
  {
    return replace(data() + pos, data() + pos + capped_length(pos, n1), n2, c);
  }

  /** Replace a part of the string.

      Replaces the characters in the range `{i1, i2)`
      with those of `str`.

      @par Precondition

      `{i1, i2)` is a valid range.

      @par Exception Safety

      Strong guarantee.

      @note The replacement is done unchecked when
      the capacity of `str` differs from that of the
      string the function is called on.

      All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @tparam M The size of the input string.

      @return `*this`

      @param i1 An iterator referring to the first character to replace.
      @param i2 An iterator referring past the end of
      the last character to replace.
      @param str The string to replace with.

      @throw std::length_error `size() + (str.size() - std::distance(i1, i2)) > max_size()`
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace(
    const_iterator i1,
    const_iterator i2,
    const basic_static_string<M, CharT, Traits>& str)
  {
    return replace_unchecked(i1, i2, str.data(), str.size());
  }

#ifndef BOOST_STATIC_STRING_DOCS
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace(
    const_iterator i1,
    const_iterator i2,
    const basic_static_string& str)
  {
    return replace(i1, i2, str.data(), str.size());
  }
#endif

  /** Replace a part of the string.

      Constructs a temporary `string_view_type` object `sv` from `t`, and
      replaces the characters in the range `{i1, i2)` with those
      of `sv`.

      @par Precondition

      `{i1, i2)` is a valid range.

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @tparam T The type of the object to convert.

      @par Constraints

      `std::is_convertible<const T&, string_view>::value &&
      !std::is_convertible<const T&, const CharT*>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value`.

      @return `*this`

      @param i1 An iterator referring to the first character to replace.
      @param i2 An iterator referring past the end of
      the last character to replace.
      @param t The object to replace with.

      @throw std::length_error `size() + (sv.size() - std::distance(i1, i2)) > max_size()`
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace(
    const_iterator i1,
    const_iterator i2,
    const T& t)
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    return replace(i1, i2, sv.data(), sv.data() + sv.size());
  }

  /** Replace a part of the string.

      Replaces the characters in the range `{i1, i2)` with those of
      `{s, s + n)`.

      @par Precondition

      `{i1, i2)` is a valid range.

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return `*this`

      @param i1 An iterator referring to the first character to replace.
      @param i2 An iterator referring past the end of
      the last character to replace.
      @param s The string to replace with.
      @param n The length of the string to replace with.

      @throw std::length_error `size() + (n - std::distance(i1, i2)) > max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace(
    const_iterator i1,
    const_iterator i2,
    const_pointer s,
    size_type n)
  {
    return replace(i1, i2, s, s + n);
  }

  /** Replace a part of the string.

      Replaces the characters in the range `{i1, i2)` with those of
      `{s, s + len)`, where the length of the string `len` is `traits_type::length(s)`.

      @par Precondition

      `{i1, i2)` shall be a valid range.

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return `*this`

      @param i1 An iterator referring to the first character to replace.
      @param i2 An iterator referring past the end of
      the last character to replace.
      @param s The string to replace with.

      @throw std::length_error `size() + (len - std::distance(i1, i2)) > max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace(
    const_iterator i1,
    const_iterator i2,
    const_pointer s)
  {
    return replace(i1, i2, s, traits_type::length(s));
  }

  /** Replace a part of the string.

      Replaces the characters in the range `{i1, i2)` with
      `n` copies of `c`.

      @par Precondition

      `{i1, i2)` is a valid range.

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return `*this`

      @param i1 An iterator referring to the first character to replace.
      @param i2 An iterator past the end of
      the last character to replace.
      @param n The number of characters to replace with.
      @param c The character to replace with.

      @throw std::length_error `size() + (n - std::distance(i1, i2)) > max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace(
    const_iterator i1,
    const_iterator i2,
    size_type n,
    value_type c);

  /** Replace a part of the string.

      Replaces the characters in the range `{i1, i2)`
      with those of `{j1, j2)`.

      @par Precondition

      `{i1, i2)` is a valid range.

      `{j1, j2)` is a valid range.

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @tparam InputIterator The type of the iterators.

      @par Constraints

      `InputIterator` satisfies __InputIterator__ and does not
      satisfy __ForwardIterator__.

      @return `*this`

      @param i1 An iterator referring to the first character to replace.
      @param i2 An iterator referring past the end of
      the last character to replace.
      @param j1 An iterator referring to the first character to replace with.
      @param j2 An iterator referring past the end of
      the last character to replace with.

      @throw std::length_error `size() + (inserted - std::distance(i1, i2)) > max_size()`
  */
  template<typename InputIterator>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
#ifdef BOOST_STATIC_STRING_DOCS
  basic_static_string&
#else
  typename std::enable_if<
    detail::is_input_iterator<
      InputIterator>::value &&
        !detail::is_forward_iterator<
          InputIterator>::value,
            basic_static_string<N, CharT, Traits>&>::type
#endif
  replace(
    const_iterator i1,
    const_iterator i2,
    InputIterator j1,
    InputIterator j2);

#ifndef BOOST_STATIC_STRING_DOCS
  template<typename ForwardIterator>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  typename std::enable_if<
    detail::is_forward_iterator<
      ForwardIterator>::value,
        basic_static_string<N, CharT, Traits>&>::type
  replace(
    const_iterator i1,
    const_iterator i2,
    ForwardIterator j1,
    ForwardIterator j2);
#endif

  /** Replace a part of the string.

      Replaces the characters in the range `{i1, i2)`
      with those of contained in the initializer list `il`.

      @par Precondition

      `{i1, i2)` is a valid range.

      @par Exception Safety

      Strong guarantee.

      @note All references, pointers, or iterators
      referring to contained elements are invalidated. Any
      past-the-end iterators are also invalidated.

      @return `*this`

      @param i1 An iterator referring to the first character to replace.
      @param i2 An iterator past the end of
      the last character to replace.
      @param il The initializer list to replace with.

      @throw std::length_error `size() + (il.size() - std::distance(i1, i2)) > max_size()`
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace(
    const_iterator i1,
    const_iterator i2,
    std::initializer_list<value_type> il)
  {
    return replace_unchecked(i1, i2, il.begin(), il.size());
  }

  //--------------------------------------------------------------------------
  //
  // Search
  //
  //--------------------------------------------------------------------------

  /** Find the first occurrence of a string within the string.

      Constructs a temporary `string_view_type` object `sv` from `t`, and finds
      the first occurrence of `sv` within the string starting at the index `pos`.

      @par Complexity

      Linear.

      @note An empty string is always found.

      @tparam T The type of the object to convert.

      @par Constraints

      `std::is_convertible<const T&, string_view>::value &&
      !std::is_convertible<const T&, const CharT*>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value`.

      @return The lowest index `idx` greater than or equal to `pos`
      where each element of `{sv.begin(), sv.end())` is equal to
      that of `{begin() + idx, begin() + idx + count)` if one exists,
      and @ref npos otherwise.

      @param t The string to search for.
      @param pos The index to start searching at. The default argument
      for this parameter is `0`.
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find(
    const T& t,
    size_type pos = 0) const
#ifdef BOOST_STATIC_STRING_DOCS
      noexcept(detail::is_nothrow_convertible<const T&, string_view_type>::value)
#else
      noexcept(detail::is_nothrow_convertible<const T&, detail::common_string_view_type<T, CharT, Traits>>::value)
#endif
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    return find(sv.data(), pos, sv.size());
  }

    /** Find the first occurrence of a string within the string.

      Finds the first occurrence of `str` within the
      string starting at the index `pos`.

      @par Complexity

      Linear.

      @return The lowest index `idx` greater than or equal to `pos`
      where each element of `str` is equal to that of
      `{begin() + idx, begin() + idx + str.size())`
      if one exists, and @ref npos otherwise.

      @param str The string to search for.
      @param pos The index to start searching at. The default argument for
      this parameter is `0`.
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find(
    const basic_static_string<M, CharT, Traits>& str,
    size_type pos = 0) const noexcept
  {
    return find(str.data(), pos, str.size());
  }

  /** Find the first occurrence of a string within the string.

      Finds the first occurrence of the string pointed to
      by `s` within the string starting at the index `pos`.

      @par Complexity

      Linear.

      @note An empty string is always found.

      @return The lowest index `idx` greater than or equal to `pos`
      where each element of `{s, s + n)` is equal to that of
      `{begin() + idx, begin() + idx + n)` if one exists,
      and @ref npos otherwise.

      @param s The string to search for.
      @param pos The index to start searching at.
      @param n The length of the string to search for.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find(
    const_pointer s,
    size_type pos,
    size_type n) const noexcept;

  /** Find the first occurrence of a string within the string.

      Finds the first occurrence of the string pointed to by `s`
      of length `count` within the string starting at the index `pos`,
      where `count` is `traits_type::length(s)`.

      @par Complexity

      Linear.

      @note An empty string is always found.

      @return The lowest index `idx` greater than or equal to `pos`
      where each element of `{s, s + count)` is equal to that of
      `{begin() + idx, begin() + idx + count)` if one exists,
      and @ref npos otherwise.

      @param s The string to search for.
      @param pos The index to start searching at. The default argument
      for this parameter is `0`.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find(
    const_pointer s,
    size_type pos = 0) const noexcept
  {
    return find(s, pos, traits_type::length(s));
  }

  /** Find the first occurrence of a character within the string.

      Finds the first occurrence of `c` within the string
      starting at the index `pos`.

      @par Complexity

      Linear.

      @return The index corrosponding to the first occurrence of `c` within
      `{begin() + pos, end())` if it exists, and @ref npos otherwise.

      @param c The character to search for.
      @param pos The index to start searching at. The default argument
      for this parameter is `0`.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find(
    value_type c,
    size_type pos = 0) const noexcept
  {
    return find(&c, pos, 1);
  }


  /** Find the last occurrence of a string within the string.

      Constructs a temporary `string_view_type` object `sv` from `t`, and finds
      the last occurrence of `sv` within the string starting before or at
      the index `pos`.

      @par Complexity

      Linear.

      @tparam T The type of the object to convert.

      @par Constraints

      `std::is_convertible<const T&, string_view>::value &&
      !std::is_convertible<const T&, const CharT*>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value`.

      @return The highest index `idx` less than or equal to `pos`
      where each element of `{sv.begin(), sv.end())` is equal to
      that of `{begin() + idx, begin() + idx + count)` if one exists,
      and @ref npos otherwise.

      @param t The string to search for.
      @param pos The index to start searching at. The default argument
      for this parameter is @ref npos.
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  rfind(
    const T& t,
    size_type pos = npos) const
#ifdef BOOST_STATIC_STRING_DOCS
      noexcept(detail::is_nothrow_convertible<const T&, string_view_type>::value)
#else
      noexcept(detail::is_nothrow_convertible<const T&, detail::common_string_view_type<T, CharT, Traits>>::value)
#endif
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    return rfind(sv.data(), pos, sv.size());
  }

  /** Find the last occurrence of a string within the string.

      Finds the last occurrence of `str` within the string
      starting before or at the index `pos`.

      @par Complexity

      Linear.

      @return The highest index `idx` less than or equal to `pos`
      where each element of `str` is equal to that
      of `{begin() + idx, begin() + idx + str.size())`
      if one exists, and @ref npos otherwise.

      @param str The string to search for.
      @param pos The index to start searching at. The default argument for
      this parameter is @ref npos.
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  rfind(
    const basic_static_string<M, CharT, Traits>& str,
    size_type pos = npos) const noexcept
  {
    return rfind(str.data(), pos, str.size());
  }

  /** Find the last occurrence of a string within the string.

      Finds the last occurrence of the string pointed to
      by `s` within the string starting before or at
      the index `pos`.

      @par Complexity

      Linear.

      @return The highest index `idx` less than or equal to `pos`
      where each element of `{s, s + n)` is equal to that of
      `{begin() + idx, begin() + idx + n)` if one exists,
      and @ref npos otherwise.

      @param s The string to search for.
      @param pos The index to start searching at.
      @param n The length of the string to search for.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  rfind(
    const_pointer s,
    size_type pos,
    size_type n) const noexcept;

  /** Find the last occurrence of a string within the string.

      Finds the last occurrence of the string pointed to by `s`
      of length `count` within the string starting before or at the
      index `pos`, where `count` is `traits_type::length(s)`.

      @par Complexity

      Linear.

      @return The highest index `idx` less than or equal to `pos`
      where each element of `{s, s + count)` is equal to that of
      `{begin() + idx, begin() + idx + count)` if one exists,
      and @ref npos otherwise.

      @param s The string to search for.
      @param pos The index to stop searching at. The default argument
      for this parameter is @ref npos.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  rfind(
    const_pointer s,
    size_type pos = npos) const noexcept
  {
    return rfind(s, pos, traits_type::length(s));
  }

  /** Find the last occurrence of a character within the string.

      Finds the last occurrence of `c` within the string
      starting before or at the index `pos`.

      @par Complexity

      Linear.

      @return The index corrosponding to the last occurrence of `c` within
      `{begin(), begin() + pos}` if it exists, and @ref npos otherwise.

      @param c The character to search for.
      @param pos The index to stop searching at. The default argument
      for this parameter is @ref npos.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  rfind(
    value_type c,
    size_type pos = npos) const noexcept
  {
    return rfind(&c, pos, 1);
  }

  /** Find the first occurrence of any of the characters within the string.

      Constructs a temporary `string_view_type` object `sv` from `t`, and finds
      the first occurrence of any of the characters in `sv`
      within the string starting at the index `pos`.

      @par Complexity

      Linear.

      @tparam T The type of the object to convert.

      @par Constraints

      `std::is_convertible<const T&, string_view>::value &&
      !std::is_convertible<const T&, const CharT*>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value`.

      @return The index corrosponding to the first occurrence of
      any of the characters in `{sv.begin(), sv.end())` within
      `{begin() + pos, end())` if it exists, and @ref npos otherwise.

      @param t The characters to search for.
      @param pos The index to start searching at. The default argument
      for this parameter is `0`.
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_first_of(
    const T& t,
    size_type pos = 0) const
#ifdef BOOST_STATIC_STRING_DOCS
      noexcept(detail::is_nothrow_convertible<const T&, string_view_type>::value)
#else
      noexcept(detail::is_nothrow_convertible<const T&, detail::common_string_view_type<T, CharT, Traits>>::value)
#endif
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    return find_first_of(sv.data(), pos, sv.size());
  }

  /** Find the first occurrence of any of the characters within the string.

      Finds the first occurrence of any of the characters within `str` within the
      string starting at the index `pos`.

      @par Complexity

      Linear.

      @return The index corrosponding to the first occurrence of any of the characters
      of `str` within `{begin() + pos, end())` if it exists, and @ref npos otherwise.

      @param str The characters to search for.
      @param pos The index to start searching at. The default argument for
      this parameter is `0`.
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_first_of(
    const basic_static_string<M, CharT, Traits>& str,
    size_type pos = 0) const noexcept
  {
    return find_first_of(str.data(), pos, str.size());
  }

  /** Find the first occurrence of any of the characters within the string.

      Finds the first occurrence of any of the characters within the string pointed to
      by `s` within the string starting at the index `pos`.

      @par Complexity

      Linear.

      @return The index corrosponding to the first occurrence
      of any of the characters in `{s, s + n)` within `{begin() + pos, end())`
      if it exists, and @ref npos otherwise.

      @param s The characters to search for.
      @param pos The index to start searching at.
      @param n The length of the string to search for.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_first_of(
    const_pointer s,
    size_type pos,
    size_type n) const noexcept;

  /** Find the first occurrence of any of the characters within the string.

      Finds the first occurrence of the any of the characters within string
      pointed to by `s` of length `count` within the string starting at the
      index `pos`, where `count` is `traits_type::length(s)`.

      @par Complexity

      Linear.

      @return The index corrosponding to the first occurrence of any of
      the characters in `{s, s + count)` within
      `{begin() + pos, end())` if it exists, and @ref npos otherwise.

      @param s The characters to search for.
      @param pos The index to start searching at. The default argument
      for this parameter is `0`.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_first_of(
    const_pointer s,
    size_type pos = 0) const noexcept
  {
    return find_first_of(s, pos, traits_type::length(s));
  }

  /** Find the first occurrence of a character within the string.

      Finds the first occurrence of `c` within the string
      starting at the index `pos`.

      @par Complexity

      Linear.

      @return The index corrosponding to the first occurrence of `c` within
      `{begin() + pos, end())` if it exists, and @ref npos otherwise.

      @param c The character to search for.
      @param pos The index to start searching at. The default argument
      for this parameter is `0`.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_first_of(
    value_type c,
    size_type pos = 0) const noexcept
  {
    return find_first_of(&c, pos, 1);
  }

  /** Find the last occurrence of any of the characters within the string.

      Constructs a temporary `string_view_type` object `sv` from `t`, and finds
      the last occurrence of any of the characters in `sv`
      within the string before or at the index `pos`.

      @par Complexity

      Linear.

      @tparam T The type of the object to convert.

      @par Constraints

      `std::is_convertible<const T&, string_view>::value &&
      !std::is_convertible<const T&, const CharT*>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value`.

      @return The index corrosponding to the last occurrence of
      any of the characters in `{sv.begin(), sv.end())` within
      `{begin(), begin() + pos}` if it exists, and @ref npos otherwise.

      @param t The characters to search for.
      @param pos The index to stop searching at. The default argument
      for this parameter is @ref npos.
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_last_of(
    const T& t,
    size_type pos = npos) const
#ifdef BOOST_STATIC_STRING_DOCS
      noexcept(detail::is_nothrow_convertible<const T&, string_view_type>::value)
#else
      noexcept(detail::is_nothrow_convertible<const T&, detail::common_string_view_type<T, CharT, Traits>>::value)
#endif
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    return find_last_of(sv.data(), pos, sv.size());
  }

  /** Find the last occurrence of any of the characters within the string.

      Finds the last occurrence of any of the characters within `str` within the
      string starting before or at the index `pos`.

      @par Complexity

      Linear.

      @return The index corrosponding to the last occurrence of any of the characters
      of `str` within `{begin(), begin() + pos}` if it exists, and @ref npos otherwise.

      @param str The characters to search for.
      @param pos The index to stop searching at. The default argument for
      this parameter is @ref npos.
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_last_of(
    const basic_static_string<M, CharT, Traits>& str,
    size_type pos = npos) const noexcept
  {
    return find_last_of(str.data(), pos, str.size());
  }

  /** Find the last occurrence of any of the characters within the string.

      Finds the last occurrence of any of the characters within the string pointed to
      by `s` within the string before or at the index `pos`.

      @par Complexity

      Linear.

      @return The index corrosponding to the last occurrence
      of any of the characters in `{s, s + n)` within `{begin(), begin() + pos}`
      if it exists, and @ref npos otherwise.

      @param s The characters to search for.
      @param pos The index to stop searching at.
      @param n The length of the string to search for.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_last_of(
    const_pointer s,
    size_type pos,
    size_type n) const noexcept;

  /** Find the last occurrence of any of the characters within the string.

      Finds the last occurrence of any of the characters within the string pointed to
      by `s` of length `count` within the string before or at the index `pos`,
      where `count` is `traits_type::length(s)`.

      @par Complexity

      Linear.

      @return The index corrosponding to the last occurrence
      of any of the characters in `{s, s + count)` within `{begin(), begin() + pos}`
      if it exists, and @ref npos otherwise.

      @param s The characters to search for.
      @param pos The index to stop searching at. The default argument for
      this parameter is @ref npos.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_last_of(
    const_pointer s,
    size_type pos = npos) const noexcept
  {
    return find_last_of(s, pos, traits_type::length(s));
  }

  /** Find the last occurrence of a character within the string.

      Finds the last occurrence of `c` within the string
      before or at the index `pos`.

      @par Complexity

      Linear.

      @return The index corrosponding to the last occurrence of `c` within
      `{begin(), begin() + pos}` if it exists, and @ref npos otherwise.

      @param c The character to search for.
      @param pos The index to stop searching at. The default argument
      for this parameter is @ref npos.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_last_of(
    value_type c,
    size_type pos = npos) const noexcept
  {
    return find_last_of(&c, pos, 1);
  }

  /** Find the first occurrence of a character not within the string.

      Constructs a temporary `string_view_type` object `sv` from `t`, and finds
      the first character that is not within `sv`, starting at the index `pos`.

      @par Complexity

      Linear.

      @tparam T The type of the object to convert.

      @par Constraints

      `std::is_convertible<const T&, string_view>::value &&
      !std::is_convertible<const T&, const CharT*>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value`.

      @return The index corrosponding to the first occurrence of
      a character that is not in `{sv.begin(), sv.end())` within
      `{begin() + pos, end())` if it exists, and @ref npos otherwise.

      @param t The characters to ignore.
      @param pos The index to start searching at. The default argument
      for this parameter is `0`.
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_first_not_of(
    const T& t,
    size_type pos = 0) const
#ifdef BOOST_STATIC_STRING_DOCS
      noexcept(detail::is_nothrow_convertible<const T&, string_view_type>::value)
#else
      noexcept(detail::is_nothrow_convertible<const T&, detail::common_string_view_type<T, CharT, Traits>>::value)
#endif
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    return find_first_not_of(sv.data(), pos, sv.size());
  }

  /** Find the first occurrence of any of the characters not within the string.

      Finds the first occurrence of a character that is not within `str`
      within the string starting at the index `pos`.

      @par Complexity

      Linear.

      @return The index corrosponding to the first character of `{begin() + pos, end())`
      that is not within `str` if it exists, and @ref npos otherwise.

      @param str The characters to ignore.
      @param pos The index to start searching at. The default argument for
      this parameter is `0`.
  */
  template<std::size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_first_not_of(
    const basic_static_string<M, CharT, Traits>& str,
    size_type pos = 0) const noexcept
  {
    return find_first_not_of(str.data(), pos, str.size());
  }

  /** Find the first occurrence of any of the characters not within the string.

      Finds the first occurrence of a character that is not within the string
      pointed to by `s` within the string starting at the index `pos`.

      @par Complexity

      Linear.

      @return The index corrosponding to the first character of `{begin() + pos, end())`
      that is not within `{s, s + n)` if it exists, and @ref npos otherwise.

      @param s The characters to ignore.
      @param pos The index to start searching at. The default argument for
      this parameter is `0`.
      @param n The length of the characters to ignore.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_first_not_of(
    const_pointer s,
    size_type pos,
    size_type n) const noexcept;

  /** Find the first occurrence of any of the characters not within the string.

      Finds the first occurrence of a character that is not within the string
      pointed to by `s` of length `count` within the string starting
      at the index `pos`, where `count` is `traits_type::length(s)`.

      @par Complexity

      Linear.

      @return The index corrosponding to the first character of `{begin() + pos, end())`
      that is not within `{s, s + count)` if it exists, and @ref npos otherwise.

      @param s The characters to ignore.
      @param pos The index to start searching at. The default argument for
      this parameter is `0`.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_first_not_of(
    const_pointer s,
    size_type pos = 0) const noexcept
  {
    return find_first_not_of(s, pos, traits_type::length(s));
  }

  /** Find the first occurrence of a character not equal to `c`.

      Finds the first occurrence of a character that is not equal
      to `c`.

      @par Complexity

      Linear.

      @return The index corrosponding to the first character of `{begin() + pos, end())`
      that is not equal to `c` if it exists, and @ref npos otherwise.

      @param c The character to ignore.
      @param pos The index to start searching at. The default argument for
      this parameter is `0`.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_first_not_of(
    value_type c,
    size_type pos = 0) const noexcept
  {
    return find_first_not_of(&c, pos, 1);
  }

  /** Find the last occurrence of a character not within the string.

      Constructs a temporary `string_view_type` object `sv` from `t`, and finds
      the last character that is not within `sv`, starting at the index `pos`.

      @par Complexity

      Linear.

      @tparam T The type of the object to convert.

      @par Constraints

      `std::is_convertible<const T&, string_view>::value &&
      !std::is_convertible<const T&, const CharT*>::value &&
      !std::is_convertible<const T&, const basic_static_string&>::value`.

      @return The index corrosponding to the last occurrence of
      a character that is not in `{sv.begin(), sv.end())` within
      `{begin(), begin() + pos}` if it exists, and @ref npos otherwise.

      @param t The characters to ignore.
      @param pos The index to start searching at. The default argument
      for this parameter is @ref npos.
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
    , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_last_not_of(
    const T& t,
    size_type pos = npos) const
#ifdef BOOST_STATIC_STRING_DOCS
      noexcept(detail::is_nothrow_convertible<const T&, string_view_type>::value)
#else
      noexcept(detail::is_nothrow_convertible<const T&, detail::common_string_view_type<T, CharT, Traits>>::value)
#endif
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    return find_last_not_of(sv.data(), pos, sv.size());
  }

  /** Find the last occurrence of a character not within the string.

      Finds the last occurrence of a character that is not within `str`
      within the string before or at the index `pos`.

      @par Complexity

      Linear.

      @return The index corrosponding to the last character of `{begin(), begin() + pos}`
      that is not within `str` if it exists, and @ref npos otherwise.

      @param str The characters to ignore.
      @param pos The index to stop searching at. The default argument for
      this parameter is @ref npos.
  */
  template<size_t M>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_last_not_of(
    const basic_static_string<M, CharT, Traits>& str,
    size_type pos = npos) const noexcept
  {
    return find_last_not_of(str.data(), pos, str.size());
  }

  /** Find the last occurrence of a character not within the string.

      Finds the last occurrence of a character that is not within the
      string pointed to by `s` within the string before or at the index `pos`.

      @par Complexity

      Linear.

      @return The index corrosponding to the last character of `{begin(), begin() + pos}`
      that is not within `{s, s + n)` if it exists, and @ref npos otherwise.

      @param s The characters to ignore.
      @param pos The index to stop searching at. The default argument for
      this parameter is @ref npos.
      @param n The length of the characters to ignore.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_last_not_of(
    const_pointer s,
    size_type pos,
    size_type n) const noexcept;


  /** Find the last occurrence of a character not within the string.

      Finds the last occurrence of a character that is not within the
      string pointed to by `s` of length `count` within the string
      before or at the index `pos`, where `count` is `traits_type::length(s)`.

      @par Complexity

      Linear.

      @return The index corrosponding to the last character of `{begin(), begin() + pos}`
      that is not within `{s, s + count)` if it exists, and @ref npos otherwise.

      @param s The characters to ignore.
      @param pos The index to stop searching at. The default argument for
      this parameter is @ref npos.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_last_not_of(
    const_pointer s,
    size_type pos = npos) const noexcept
  {
    return find_last_not_of(s, pos, traits_type::length(s));
  }

  /** Find the last occurrence of a character not equal to `c`.

      Finds the last occurrence of a character that is not equal
      to `c` before or at the index `pos`.

      @par Complexity

      Linear.

      @return The index corrosponding to the last character of `{begin(), begin() + pos}`
      that is not equal to `c` if it exists, and @ref npos otherwise.

      @param c The character to ignore.
      @param pos The index to start searching at. The default argument for
      this parameter is @ref npos.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  find_last_not_of(
    value_type c,
    size_type pos = npos) const noexcept
  {
    return find_last_not_of(&c, pos, 1);
  }

  /** Return whether the string begins with a string.

      Returns `true` if the string begins with `s`, and `false` otherwise.

      @par Complexity

      Linear.

      @param t The string view to check for.
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
            , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  bool
  starts_with(
    T const& t) const noexcept
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    const size_type len = sv.size();
    return size() >= len && !traits_type::compare(data(), sv.data(), len);
  }

  /** Return whether the string begins with a character.

      Returns `true` if the string begins with `c`, and `false` otherwise.

      @par Complexity

      Constant.

      @param c The character to check for.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  bool
  starts_with(
    value_type c) const noexcept
  {
    return !empty() && traits_type::eq(front(), c);
  }

  /** Return whether the string begins with a string.

      Returns `true` if the string begins with the string
      pointed to be `s` of length `traits_type::length(s)`,
      and `false` otherwise.

      @par Complexity

      Linear.

      @param s The string to check for.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  bool
  starts_with(
    const_pointer s) const noexcept
  {
    const size_type len = traits_type::length(s);
    return size() >= len && !traits_type::compare(data(), s, len);
  }

  /** Return whether the string ends with a string.

      Returns `true` if the string ends with `s`, and `false` otherwise.

      @par Complexity

      Linear.

      @param t The string view to check for.
  */
  template<typename T
#ifndef BOOST_STATIC_STRING_DOCS
            , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
  >
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  bool
  ends_with(
    T const& t) const noexcept
  {
    detail::common_string_view_type<T, CharT, Traits> sv = t;
    const size_type len = sv.size();
    return size() >= len && !traits_type::compare(data() + (size() - len), sv.data(), len);
  }

  /** Return whether the string ends with a character.

      Returns `true` if the string ends with `c`, and `false` otherwise.

      @par Complexity

      Constant.

      @param c The character to check for.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  bool
  ends_with(
    value_type c) const noexcept
  {
    return !empty() && traits_type::eq(back(), c);
  }

  /** Return whether the string ends with a string.

      Returns `true` if the string ends with the string
      pointed to be `s` of length `traits_type::length(s)`,
      and `false` otherwise.

      @par Complexity

      Linear.

      @param s The string to check for.
  */
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  bool
  ends_with(
    const_pointer s) const noexcept
  {
    const size_type len = traits_type::length(s);
    return size() >= len && !traits_type::compare(data() + (size() - len), s, len);
  }

private:
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  term() noexcept
  {
    this->term_impl();
    return *this;
  }

  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  assign_char(value_type ch, std::true_type) noexcept
  {
    this->set_size(1);
    traits_type::assign(data()[0], ch);
    return term();
  }

  BOOST_STATIC_STRING_NORETURN
  basic_static_string&
  assign_char(value_type, std::false_type)
  {
    detail::throw_exception<std::length_error>("max_size() == 0");
    // This eliminates any potential warnings
#ifdef BOOST_STATIC_STRING_NO_NORETURN
    return *this;
#endif
  }

  // Returns the size of data read from input iterator. Read data begins at data() + size() + 1.
  template<typename InputIterator>
  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  read_back(
    bool overwrite_null,
    InputIterator first,
    InputIterator last);

  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace_unchecked(
    size_type pos,
    size_type n1,
    const_pointer s,
    size_type n2)
  {
    if (pos > size())
      detail::throw_exception<std::out_of_range>(
        "pos > size()");
    return replace_unchecked(data() + pos, data() + pos + capped_length(pos, n1), s, n2);
  }

  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  replace_unchecked(
    const_iterator i1,
    const_iterator i2,
    const_pointer s,
    size_type n2);

  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  insert_unchecked(
    size_type index,
    const_pointer s,
    size_type count)
  {
    if (index > size())
      detail::throw_exception<std::out_of_range>(
        "index > size()");
    insert_unchecked(data() + index, s, count);
    return *this;
  }

  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  iterator
  insert_unchecked(
    const_iterator pos,
    const_pointer s,
    size_type count);

  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  basic_static_string&
  assign_unchecked(
    const_pointer s,
    size_type count) noexcept
  {
    this->set_size(count);
    traits_type::copy(data(), s, size() + 1);
    return *this;
  }

  BOOST_STATIC_STRING_CPP14_CONSTEXPR
  size_type
  capped_length(
    size_type index,
    size_type length) const
  {
    if (index > size())
      detail::throw_exception<std::out_of_range>(
        "index > size()");
    return (std::min)(size() - index, length);
  }
};

//------------------------------------------------------------------------------
//
// Non-member functions
//
//------------------------------------------------------------------------------

template<
  std::size_t N, std::size_t M,
  typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator==(
  const basic_static_string<N, CharT, Traits>& lhs,
  const basic_static_string<M, CharT, Traits>& rhs)
{
  return lhs.compare(rhs) == 0;
}

template<
  std::size_t N, std::size_t M,
  typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator!=(
  const basic_static_string<N, CharT, Traits>& lhs,
  const basic_static_string<M, CharT, Traits>& rhs)
{
  return lhs.compare(rhs) != 0;
}

template<
  std::size_t N, std::size_t M,
  typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator<(
  const basic_static_string<N, CharT, Traits>& lhs,
  const basic_static_string<M, CharT, Traits>& rhs)
{
  return lhs.compare(rhs) < 0;
}

template<
  std::size_t N, std::size_t M,
  typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator<=(
  const basic_static_string<N, CharT, Traits>& lhs,
  const basic_static_string<M, CharT, Traits>& rhs)
{
  return lhs.compare(rhs) <= 0;
}

template<
  std::size_t N, std::size_t M,
  typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator>(
  const basic_static_string<N, CharT, Traits>& lhs,
  const basic_static_string<M, CharT, Traits>& rhs)
{
  return lhs.compare(rhs) > 0;
}

template<
  std::size_t N, std::size_t M,
  typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator>=(
  const basic_static_string<N, CharT, Traits>& lhs,
  const basic_static_string<M, CharT, Traits>& rhs)
{
  return lhs.compare(rhs) >= 0;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator==(
  const CharT* lhs,
  const basic_static_string<N, CharT, Traits>& rhs)
{
  return detail::lexicographical_compare<CharT, Traits>(
    lhs, Traits::length(lhs),
    rhs.data(), rhs.size()) == 0;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator==(
  const basic_static_string<N, CharT, Traits>& lhs,
  const CharT* rhs)
{
  return detail::lexicographical_compare<CharT, Traits>(
    lhs.data(), lhs.size(),
    rhs, Traits::length(rhs)) == 0;
}

template<std::size_t N, typename CharT, typename Traits, class T
#ifndef BOOST_STATIC_STRING_DOCS
      , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
    >
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator==(
  const T& lhs,
  const basic_static_string<N, CharT, Traits>& rhs)
{
  detail::common_string_view_type<T, CharT, Traits> lhsv = lhs;
  return detail::lexicographical_compare<CharT, Traits>(
    lhsv.data(), lhsv.size(),
    rhs.data(), rhs.size()) == 0;
}

template<std::size_t N, typename CharT, typename Traits, class T
#ifndef BOOST_STATIC_STRING_DOCS
      , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
    >
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator==(
  const basic_static_string<N, CharT, Traits>& lhs,
  const T& rhs)
{
  detail::common_string_view_type<T, CharT, Traits> rhsv = rhs;
  return detail::lexicographical_compare<CharT, Traits>(
    lhs.data(), lhs.size(),
    rhsv.data(), rhsv.size()) == 0;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator!=(
  const CharT* lhs,
  const basic_static_string<N, CharT, Traits>& rhs)
{
  return detail::lexicographical_compare<CharT, Traits>(
    lhs, Traits::length(lhs),
    rhs.data(), rhs.size()) != 0;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator!=(
  const basic_static_string<N, CharT, Traits>& lhs,
  const CharT* rhs)
{
  return detail::lexicographical_compare<CharT, Traits>(
    lhs.data(), lhs.size(),
    rhs, Traits::length(rhs)) != 0;
}

template<std::size_t N, typename CharT, typename Traits, class T
#ifndef BOOST_STATIC_STRING_DOCS
      , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
    >
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator!=(
  const T& lhs,
  const basic_static_string<N, CharT, Traits>& rhs)
{
  detail::common_string_view_type<T, CharT, Traits> lhsv = lhs;
  return detail::lexicographical_compare<CharT, Traits>(
    lhsv.data(), lhsv.size(),
    rhs.data(), rhs.size()) != 0;
}

template<std::size_t N, typename CharT, typename Traits, class T
#ifndef BOOST_STATIC_STRING_DOCS
      , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
    >
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator!=(
  const basic_static_string<N, CharT, Traits>& lhs,
  const T& rhs)
{
  detail::common_string_view_type<T, CharT, Traits> rhsv = rhs;
  return detail::lexicographical_compare<CharT, Traits>(
    lhs.data(), lhs.size(),
    rhsv.data(), rhsv.size()) != 0;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator<(
  const CharT* lhs,
  const basic_static_string<N, CharT, Traits>& rhs)
{
  return detail::lexicographical_compare<CharT, Traits>(
    lhs, Traits::length(lhs),
    rhs.data(), rhs.size()) < 0;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator<(
  const basic_static_string<N, CharT, Traits>& lhs,
  const CharT* rhs)
{
  return detail::lexicographical_compare<CharT, Traits>(
    lhs.data(), lhs.size(),
    rhs, Traits::length(rhs)) < 0;
}

template<std::size_t N, typename CharT, typename Traits, class T
#ifndef BOOST_STATIC_STRING_DOCS
      , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
    >
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator<(
  const T& lhs,
  const basic_static_string<N, CharT, Traits>& rhs)
{
  detail::common_string_view_type<T, CharT, Traits> lhsv = lhs;
  return detail::lexicographical_compare<CharT, Traits>(
    lhsv.data(), lhsv.size(),
    rhs.data(), rhs.size()) < 0;
}

template<std::size_t N, typename CharT, typename Traits, class T
#ifndef BOOST_STATIC_STRING_DOCS
      , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
    >
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator<(
  const basic_static_string<N, CharT, Traits>& lhs,
  const T& rhs)
{
  detail::common_string_view_type<T, CharT, Traits> rhsv = rhs;
  return detail::lexicographical_compare<CharT, Traits>(
    lhs.data(), lhs.size(),
    rhsv.data(), rhsv.size()) < 0;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator<=(
  const CharT* lhs,
  const basic_static_string<N, CharT, Traits>& rhs)
{
  return detail::lexicographical_compare<CharT, Traits>(
    lhs, Traits::length(lhs),
    rhs.data(), rhs.size()) <= 0;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator<=(
  const basic_static_string<N, CharT, Traits>& lhs,
  const CharT* rhs)
{
  return detail::lexicographical_compare<CharT, Traits>(
    lhs.data(), lhs.size(),
    rhs, Traits::length(rhs)) <= 0;
}

template<std::size_t N, typename CharT, typename Traits, class T
#ifndef BOOST_STATIC_STRING_DOCS
      , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
    >
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator<=(
  const T& lhs,
  const basic_static_string<N, CharT, Traits>& rhs)
{
  detail::common_string_view_type<T, CharT, Traits> lhsv = lhs;
  return detail::lexicographical_compare<CharT, Traits>(
    lhsv.data(), lhsv.size(),
    rhs.data(), rhs.size()) <= 0;
}

template<std::size_t N, typename CharT, typename Traits, class T
#ifndef BOOST_STATIC_STRING_DOCS
      , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
    >
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator<=(
  const basic_static_string<N, CharT, Traits>& lhs,
  const T& rhs)
{
  detail::common_string_view_type<T, CharT, Traits> rhsv = rhs;
  return detail::lexicographical_compare<CharT, Traits>(
    lhs.data(), lhs.size(),
    rhsv.data(), rhsv.size()) <= 0;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator>(
  const CharT* lhs,
  const basic_static_string<N, CharT, Traits>& rhs)
{
  return detail::lexicographical_compare<CharT, Traits>(
    lhs, Traits::length(lhs),
    rhs.data(), rhs.size()) > 0;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator>(
  const basic_static_string<N, CharT, Traits>& lhs,
  const CharT* rhs)
{
  return detail::lexicographical_compare<CharT, Traits>(
    lhs.data(), lhs.size(),
    rhs, Traits::length(rhs)) > 0;
}

template<std::size_t N, typename CharT, typename Traits, class T
#ifndef BOOST_STATIC_STRING_DOCS
      , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
    >
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator>(
  const T& lhs,
  const basic_static_string<N, CharT, Traits>& rhs)
{
  detail::common_string_view_type<T, CharT, Traits> lhsv = lhs;
  return detail::lexicographical_compare<CharT, Traits>(
    lhsv.data(), lhsv.size(),
    rhs.data(), rhs.size()) > 0;
}

template<std::size_t N, typename CharT, typename Traits, class T
#ifndef BOOST_STATIC_STRING_DOCS
      , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
    >
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator>(
  const basic_static_string<N, CharT, Traits>& lhs,
  const T& rhs)
{
  detail::common_string_view_type<T, CharT, Traits> rhsv = rhs;
  return detail::lexicographical_compare<CharT, Traits>(
    lhs.data(), lhs.size(),
    rhsv.data(), rhsv.size()) > 0;
}


template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator>=(
  const CharT* lhs,
  const basic_static_string<N, CharT, Traits>& rhs)
{
  return detail::lexicographical_compare<CharT, Traits>(
    lhs, Traits::length(lhs),
    rhs.data(), rhs.size()) >= 0;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator>=(
  const basic_static_string<N, CharT, Traits>& lhs,
  const CharT* rhs)
{
  return detail::lexicographical_compare<CharT, Traits>(
    lhs.data(), lhs.size(),
    rhs, Traits::length(rhs)) >= 0;
}

template<std::size_t N, typename CharT, typename Traits, class T
#ifndef BOOST_STATIC_STRING_DOCS
      , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
    >
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator>=(
  const T& lhs,
  const basic_static_string<N, CharT, Traits>& rhs)
{
  detail::common_string_view_type<T, CharT, Traits> lhsv = lhs;
  return detail::lexicographical_compare<CharT, Traits>(
    lhsv.data(), lhsv.size(),
    rhs.data(), rhs.size()) >= 0;
}

template<std::size_t N, typename CharT, typename Traits, class T
#ifndef BOOST_STATIC_STRING_DOCS
      , typename = detail::enable_if_viewable_t<N, T, CharT, Traits>
#endif
    >
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
bool
operator>=(
  const basic_static_string<N, CharT, Traits>& lhs,
  const T& rhs)
{
  detail::common_string_view_type<T, CharT, Traits> rhsv = rhs;
  return detail::lexicographical_compare<CharT, Traits>(
    lhs.data(), lhs.size(),
    rhsv.data(), rhsv.size()) >= 0;
}

template<
  std::size_t N, std::size_t M,
  typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
basic_static_string<N + M, CharT, Traits>
operator+(
  const basic_static_string<N, CharT, Traits>& lhs,
  const basic_static_string<M, CharT, Traits>& rhs)
{
  return basic_static_string<N + M, CharT, Traits>(lhs) += rhs;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
basic_static_string<N + 1, CharT, Traits>
operator+(
  const basic_static_string<N, CharT, Traits>& lhs,
  CharT rhs)
{
  return basic_static_string<N + 1, CharT, Traits>(lhs) += rhs;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
basic_static_string<N + 1, CharT, Traits>
operator+(
  CharT lhs,
  const basic_static_string<N, CharT, Traits>& rhs)
{
  // The cast to std::size_t is needed here since 0 is a null pointer constant
  return basic_static_string<N + 1, CharT, Traits>(rhs).insert(
    std::size_t(0), 1, lhs);
}

// Add a null terminated character array to a string.
template<
  std::size_t N, std::size_t M,
  typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
basic_static_string<N + M, CharT, Traits>
operator+(
  const basic_static_string<N, CharT, Traits>& lhs,
  const CharT(&rhs)[M])
{
  return basic_static_string<N + M, CharT, Traits>(lhs).append(+rhs);
}

// Add a string to a null terminated character array.
template<
  std::size_t N, std::size_t M,
  typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
basic_static_string<N + M, CharT, Traits>
operator+(
  const CharT(&lhs)[N],
  const basic_static_string<M, CharT, Traits>& rhs)
{
  // The cast to std::size_t is needed here since 0 is a null pointer constant
  return basic_static_string<N + M, CharT, Traits>(rhs).insert(
    std::size_t(0), +lhs);
}

//------------------------------------------------------------------------------
//
// erase_if
//
//------------------------------------------------------------------------------

template<
    std::size_t N, typename CharT,
    typename Traits, typename UnaryPredicate>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
typename
basic_static_string<N, CharT, Traits>::size_type
erase_if(
    basic_static_string<N, CharT, Traits>& str,
    UnaryPredicate pred)
{
  auto first = str.begin();
  for (auto it = first; it != str.end(); ++it)
    if (!pred(*it))
      *first++ = std::move(*it);
  const auto count = str.end() - first;
  str.erase(first, str.end());
  return count;
}

//------------------------------------------------------------------------------
//
// swap
//
//------------------------------------------------------------------------------

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
void
swap(
  basic_static_string<N, CharT, Traits>& lhs,
  basic_static_string<N, CharT, Traits>& rhs)
{
  lhs.swap(rhs);
}

template<
  std::size_t N, std::size_t M,
  typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
inline
void
swap(
  basic_static_string<N, CharT, Traits>& lhs,
  basic_static_string<M, CharT, Traits>& rhs)
{
  lhs.swap(rhs);
}

//------------------------------------------------------------------------------
//
// Input/Output
//
//------------------------------------------------------------------------------

template<std::size_t N, typename CharT, typename Traits>
inline
std::basic_ostream<CharT, Traits>&
operator<<(
  std::basic_ostream<CharT, Traits>& os,
  const basic_static_string<N, CharT, Traits>& s)
{
#ifdef BOOST_STATIC_STRING_HAS_ANY_STRING_VIEW
  return os << basic_string_view<CharT, Traits>(s.data(), s.size());
#else
  for (auto c: s)
    os << c;
  return os;
#endif
}

//------------------------------------------------------------------------------
//
// Numeric conversions
//
//------------------------------------------------------------------------------

// Signed overloads have a + 2, one for the missing digit,
// and one for the sign.

// Unsigned overloads have a + 1, for the missing digit.

// Floating point overloads have a +8 (for float), + 8
// (for double), and +10 for long double (that accounts
// for the sign of the integral part, the missing digit,
// the decimal point, the sign of the exponent, the 'e'
// and up to two, three or five digits of exponent---float
// uses the same value as double, because we sometimes
// reuse the conversion from double for floats).

/// Converts `value` to a `static_string`
static_string<std::numeric_limits<int>::digits10 + 2>
inline
to_static_string(int value) noexcept
{
  return detail::to_static_string_int_impl<
    std::numeric_limits<int>::digits10 + 2>(value);
}

/// Converts `value` to a `static_string`
static_string<std::numeric_limits<long>::digits10 + 2>
inline
to_static_string(long value) noexcept
{
  return detail::to_static_string_int_impl<
    std::numeric_limits<long>::digits10 + 2>(value);
}

/// Converts `value` to a `static_string`
static_string<std::numeric_limits<long long>::digits10 + 2>
inline
to_static_string(long long value) noexcept
{
  return detail::to_static_string_int_impl<
    std::numeric_limits<long long>::digits10 + 2>(value);
}

/// Converts `value` to a `static_string`
static_string<std::numeric_limits<unsigned int>::digits10 + 1>
inline
to_static_string(unsigned int value) noexcept
{
  return detail::to_static_string_int_impl<
    std::numeric_limits<unsigned int>::digits10 + 1>(value);
}

/// Converts `value` to a `static_string`
static_string<std::numeric_limits<unsigned long>::digits10 + 1>
inline
to_static_string(unsigned long value) noexcept
{
  return detail::to_static_string_int_impl<
    std::numeric_limits<unsigned long>::digits10 + 1>(value);
}

/// Converts `value` to a `static_string`
static_string<std::numeric_limits<unsigned long long>::digits10 + 1>
inline
to_static_string(unsigned long long value) noexcept
{
  return detail::to_static_string_int_impl<
    std::numeric_limits<unsigned long long>::digits10 + 1>(value);
}

/// Converts `value` to a `static_string`
static_string<std::numeric_limits<float>::max_digits10 + 8>
inline
to_static_string(float value) noexcept
{
  return detail::to_static_string_float_impl<
    std::numeric_limits<float>::max_digits10 + 8>(value);
}

/// Converts `value` to a `static_string`
static_string<std::numeric_limits<double>::max_digits10 + 8>
inline
to_static_string(double value) noexcept
{
  return detail::to_static_string_float_impl<
    std::numeric_limits<double>::max_digits10 + 8>(value);
}

/// Converts `value` to a `static_string`
static_string<std::numeric_limits<long double>::max_digits10 + 10>
inline
to_static_string(long double value) noexcept
{
  return detail::to_static_string_float_impl<
    std::numeric_limits<long double>::max_digits10 + 10>(value);
}

#ifdef BOOST_STATIC_STRING_HAS_WCHAR
/// Converts `value` to a `static_wstring`
static_wstring<std::numeric_limits<int>::digits10 + 2>
inline
to_static_wstring(int value) noexcept
{
  return detail::to_static_wstring_int_impl<
    std::numeric_limits<int>::digits10 + 2>(value);
}

/// Converts `value` to a `static_wstring`
static_wstring<std::numeric_limits<long>::digits10 + 2>
inline
to_static_wstring(long value) noexcept
{
  return detail::to_static_wstring_int_impl<
    std::numeric_limits<long>::digits10 + 2>(value);
}

/// Converts `value` to a `static_wstring`
static_wstring<std::numeric_limits<long long>::digits10 + 2>
inline
to_static_wstring(long long value) noexcept
{
  return detail::to_static_wstring_int_impl<
    std::numeric_limits<long long>::digits10 + 2>(value);
}

/// Converts `value` to a `static_wstring`
static_wstring<std::numeric_limits<unsigned int>::digits10 + 1>
inline
to_static_wstring(unsigned int value) noexcept
{
  return detail::to_static_wstring_int_impl<
    std::numeric_limits<unsigned int>::digits10 + 1>(value);
}

/// Converts `value` to a `static_wstring`
static_wstring<std::numeric_limits<unsigned long>::digits10 + 1>
inline
to_static_wstring(unsigned long value) noexcept
{
  return detail::to_static_wstring_int_impl<
    std::numeric_limits<unsigned long>::digits10 + 1>(value);
}

/// Converts `value` to a `static_wstring`
static_wstring<std::numeric_limits<unsigned long long>::digits10 + 1>
inline
to_static_wstring(unsigned long long value) noexcept
{
  return detail::to_static_wstring_int_impl<
    std::numeric_limits<unsigned long long>::digits10 + 1>(value);
}

/// Converts `value` to a `static_wstring`
static_wstring<std::numeric_limits<float>::max_digits10 + 8>
inline
to_static_wstring(float value) noexcept
{
  return detail::to_static_wstring_float_impl<
    std::numeric_limits<float>::max_digits10 + 8>(value);
}

/// Converts `value` to a `static_wstring`
static_wstring<std::numeric_limits<double>::max_digits10 + 8>
inline
to_static_wstring(double value) noexcept
{
  return detail::to_static_wstring_float_impl<
    std::numeric_limits<double>::max_digits10 + 8>(value);
}

/// Converts `value` to a `static_wstring`
static_wstring<std::numeric_limits<long double>::max_digits10 + 10>
inline
to_static_wstring(long double value) noexcept
{
  return detail::to_static_wstring_float_impl<
    std::numeric_limits<long double>::max_digits10 + 10>(value);
}
#endif

//------------------------------------------------------------------------------
//
// Deduction Guides
//
//------------------------------------------------------------------------------

#ifdef BOOST_STATIC_STRING_USE_DEDUCT
template<std::size_t N, typename CharT>
basic_static_string(const CharT(&)[N]) ->
  basic_static_string<N, CharT, std::char_traits<CharT>>;
#endif

//------------------------------------------------------------------------------
//
// Hashing
//
//------------------------------------------------------------------------------

#ifndef BOOST_STATIC_STRING_STANDALONE
/// hash_value overload for Boost.Container_Hash
template <std::size_t N,
  typename CharT,
  typename Traits>
std::size_t
hash_value(
  const basic_static_string<N, CharT, Traits>& str)
{
  return boost::hash_range(str.begin(), str.end());
}
#endif
} // static_strings

//------------------------------------------------------------------------------
//
// using Declarations
//
//------------------------------------------------------------------------------

using static_strings::static_string;
#ifdef BOOST_STATIC_STRING_HAS_WCHAR
using static_strings::static_wstring;
#endif
using static_strings::static_u16string;
using static_strings::static_u32string;
} // boost

/// std::hash partial specialization for basic_static_string
namespace std {

template<std::size_t N, typename CharT, typename Traits>
struct hash<
#ifdef BOOST_STATIC_STRING_DOCS
  basic_static_string
#else
  boost::static_strings::basic_static_string<N, CharT, Traits>
#endif
  >
{
  std::size_t
  operator()(
    const boost::static_strings::basic_static_string<N, CharT, Traits>& str) const noexcept
  {
#if !defined(BOOST_STATIC_STRING_STANDALONE)
    return boost::hash_range(str.begin(), str.end());
#elif defined(BOOST_STATIC_STRING_HAS_ANY_STRING_VIEW)
    using view_type = typename
      boost::static_strings::basic_string_view<CharT, Traits>;
    return std::hash<view_type>()(view_type(str.data(), str.size()));
#else
    std::size_t seed = 0;
    for (CharT const& c : str)
    {
#if BOOST_STATIC_STRING_ARCH == 64
      seed += 0x9e3779b9 + std::hash<CharT>()( c );
      std::size_t const m = (std::size_t(0xe9846af) << 32) + 0x9b1a615d;
      seed ^= seed >> 32;
      seed *= m;
      seed ^= seed >> 32;
      seed *= m;
      seed ^= seed >> 28;
#elif BOOST_STATIC_STRING_ARCH == 32
      seed += 0x9e3779b9 + std::hash<CharT>()( c );
      std::size_t const m1 = 0x21f0aaad;
      std::size_t const m2 = 0x735a2d97;
      seed ^= seed >> 16;
      seed *= m1;
      seed ^= seed >> 15;
      seed *= m2;
      seed ^= seed >> 15;
#endif
    }
    return seed;
#endif
  }
};
} // std

//--------------------------------------------------------------------------
//
// Implementation
//
//--------------------------------------------------------------------------

#ifndef BOOST_STATIC_STRING_DOCS
namespace boost {
namespace static_strings {

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
assign(
  size_type count,
  value_type ch) ->
    basic_static_string&
{
  if (count > max_size())
    detail::throw_exception<std::length_error>(
      "count > max_size()");
  this->set_size(count);
  traits_type::assign(data(), size(), ch);
  return term();
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
assign(
  const_pointer s,
  size_type count) ->
    basic_static_string&
{
  if (count > max_size())
    detail::throw_exception<std::length_error>(
      "count > max_size()");
  this->set_size(count);
  traits_type::move(data(), s, size());
  return term();
}

template<std::size_t N, typename CharT, typename Traits>
template<typename InputIterator>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
assign(
  InputIterator first,
  InputIterator last) ->
    typename std::enable_if<
      detail::is_input_iterator<InputIterator>::value,
        basic_static_string&>::type
{
  auto ptr = data();
  for (std::size_t i = 0; first != last; ++first, ++ptr, ++i)
  {
    if (i >= max_size())
    {
      this->set_size(i);
      term();
      detail::throw_exception<std::length_error>("n > max_size()");
    }
    traits_type::assign(*ptr, *first);
  }
  this->set_size(ptr - data());
  return term();
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
insert(
  const_iterator pos,
  size_type count,
  value_type ch) ->
    iterator
{
  const auto curr_size = size();
  const auto curr_data = data();
  if (count > max_size() - curr_size)
    detail::throw_exception<std::length_error>(
      "count > max_size() - curr_size");
  const auto index = pos - curr_data;
  traits_type::move(&curr_data[index + count], &curr_data[index], curr_size - index + 1);
  traits_type::assign(&curr_data[index], count, ch);
  this->set_size(curr_size + count);
  return &curr_data[index];
}

template<std::size_t N, typename CharT, typename Traits>
template<typename ForwardIterator>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
insert(
  const_iterator pos,
  ForwardIterator first,
  ForwardIterator last) ->
    typename std::enable_if<
      detail::is_forward_iterator<
        ForwardIterator>::value, iterator>::type
{
  // input
  const std::size_t count = detail::distance(first, last);
  const auto first_addr = &*first;
  const auto last_addr = first_addr + count;

  // output
  const auto curr_size = size();
  const auto curr_data = data();
  const std::size_t index = pos - curr_data;
  auto dest = &curr_data[index];

  if (count > max_size() - curr_size)
    detail::throw_exception<std::length_error>(
      "count > max_size() - curr_size");

  traits_type::move(dest + count, dest, curr_size - index + 1);
  const bool inside = detail::ptr_in_range(curr_data, curr_data + curr_size, first_addr);
  if (!inside || last_addr <= pos)
  {
    detail::copy_with_traits<Traits>(first, last, dest);
  }
  else /* if (inside) */
  {
    const size_type offset = first_addr - curr_data;
    if (offset < index)
    {
      const size_type diff = index - offset;
      traits_type::copy(dest, &curr_data[offset], diff);
      traits_type::copy(&curr_data[index + diff], dest + count, count - diff);
    }
    else
    {
      auto src = &curr_data[offset + count];
      traits_type::copy(dest, src, count);
    }
  }
  this->set_size(curr_size + count);
  return curr_data + index;
}

template<std::size_t N, typename CharT, typename Traits>
template<typename InputIterator>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
insert(
  const_iterator pos,
  InputIterator first,
  InputIterator last) ->
    typename std::enable_if<
      detail::is_input_iterator<
        InputIterator>::value &&
          !detail::is_forward_iterator<
            InputIterator>::value, iterator>::type
{
  const auto curr_size = size();
  const auto curr_data = data();
  const auto count = read_back(false, first, last);
  const std::size_t index = pos - curr_data;
  std::rotate(&curr_data[index], &curr_data[curr_size + 1], &curr_data[curr_size + count + 1]);
  this->set_size(curr_size + count);
  return curr_data + index;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
erase(
  const_iterator first,
  const_iterator last) ->
    iterator
{
  const auto curr_data = data();
  const std::size_t index = first - curr_data;
  traits_type::move(&curr_data[index], last, (end() - last) + 1);
  this->set_size(size() - std::size_t(last - first));
  return curr_data + index;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
void
basic_static_string<N, CharT, Traits>::
push_back(
  value_type ch)
{
  const auto curr_size = size();
  if (curr_size >= max_size())
    detail::throw_exception<std::length_error>(
      "curr_size >= max_size()");
  traits_type::assign(data()[curr_size], ch);
  this->set_size(curr_size + 1);
  term();
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
append(
  size_type count,
  value_type ch) ->
    basic_static_string&
{
  const auto curr_size = size();
  if (count > max_size() - curr_size)
    detail::throw_exception<std::length_error>(
      "count > max_size() - size()");
  traits_type::assign(end(), count, ch);
  this->set_size(curr_size + count);
  return term();
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
append(
  const_pointer s,
  size_type count) ->
    basic_static_string&
{
  const auto curr_size = size();
  if (count > max_size() - curr_size)
    detail::throw_exception<std::length_error>(
      "count > max_size() - size()");
  traits_type::copy(end(), s, count);
  this->set_size(curr_size + count);
  return term();
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
void
basic_static_string<N, CharT, Traits>::
resize(size_type n, value_type c)
{
  if (n > max_size())
    detail::throw_exception<std::length_error>(
      "n > max_size()");
  const auto curr_size = size();
  if(n > curr_size)
    traits_type::assign(data() + curr_size, n - curr_size, c);
  this->set_size(n);
  term();
}

template<std::size_t N, typename CharT, typename Traits>
template<typename Operation>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
void
basic_static_string<N, CharT, Traits>::
resize_and_overwrite(
  size_type n,
  Operation op)
{
  if (n > max_size()) {
    detail::throw_exception<std::length_error>("n > max_size() in resize_and_overwrite()");
  }

  CharT* p = data();
  const auto new_size = std::move(op)(p, n);
  BOOST_STATIC_STRING_ASSERT(new_size >= 0 && size_type(new_size) <= n);
  this->set_size(size_type(new_size));
  term();
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
void
basic_static_string<N, CharT, Traits>::
swap(basic_static_string& s) noexcept
{
  const auto curr_size = size();
  basic_static_string tmp(s);
  s.set_size(curr_size);
  traits_type::copy(&s.data()[0], data(), curr_size + 1);
  this->set_size(tmp.size());
  traits_type::copy(data(), tmp.data(), size() + 1);
}

template<std::size_t N, typename CharT, typename Traits>
template<std::size_t M>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
void
basic_static_string<N, CharT, Traits>::
swap(basic_static_string<M, CharT, Traits>& s)
{
  const auto curr_size = size();
  if (curr_size > s.max_size())
    detail::throw_exception<std::length_error>(
      "curr_size > s.max_size()");
  if (s.size() > max_size())
    detail::throw_exception<std::length_error>(
      "s.size() > max_size()");
  basic_static_string tmp(s);
  s.set_size(curr_size);
  traits_type::copy(&s.data()[0], data(), curr_size + 1);
  this->set_size(tmp.size());
  traits_type::copy(data(), &tmp.data()[0], size() + 1);
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
replace(
  const_iterator i1,
  const_iterator i2,
  size_type n,
  value_type c) ->
    basic_static_string<N, CharT, Traits>&
{
  const auto curr_size = size();
  const auto curr_data = data();
  const std::size_t n1 = i2 - i1;
  if (n > max_size() || curr_size - n1 >= max_size() - n)
    detail::throw_exception<std::length_error>(
      "replaced string exceeds max_size()");
  const auto pos = i1 - curr_data;
  traits_type::move(&curr_data[pos + n], i2, (end() - i2) + 1);
  traits_type::assign(&curr_data[pos], n, c);
  this->set_size((curr_size - n1) + n);
  return *this;
}

template<std::size_t N, typename CharT, typename Traits>
template<typename ForwardIterator>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
replace(
  const_iterator i1,
  const_iterator i2,
  ForwardIterator j1,
  ForwardIterator j2) ->
    typename std::enable_if<
      detail::is_forward_iterator<ForwardIterator>::value,
        basic_static_string<N, CharT, Traits>&>::type
{
  const auto curr_size = size();
  const auto curr_data = data();
  const auto first_addr = &*j1;
  const std::size_t n1 = i2 - i1;
  const std::size_t n2 = detail::distance(j1, j2);
  const std::size_t pos = i1 - curr_data;
  if (n2 > max_size() || curr_size - (std::min)(n1, curr_size - pos) >= max_size() - n2)
    detail::throw_exception<std::length_error>(
      "replaced string exceeds max_size()");
  const bool inside = detail::ptr_in_range(curr_data, curr_data + curr_size, first_addr);
  if (inside && first_addr == i1 && n1 == n2)
    return *this;
  // Short circuit evaluation ensures that the pointer arithmetic is valid
  if (!inside || (inside && (first_addr + n2 <= i1)))
  {
    // source outside
    traits_type::move(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1);
    detail::copy_with_traits<Traits>(j1, j2, &curr_data[pos]);
  }
  else
  {
    // source inside
    const size_type offset = first_addr - curr_data;
    if (n2 >= n1)
    {
      // grow/unchanged
      const size_type diff = offset <= pos + n1 ? (std::min)((pos + n1) - offset, n2) : 0;
      // shift all right of splice point by n2 - n1 to the right
      traits_type::move(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1);
      // copy all before splice point
      traits_type::move(&curr_data[pos], &curr_data[offset], diff);
      // copy all after splice point
      traits_type::move(&curr_data[pos + diff], &curr_data[(offset - n1) + n2 + diff], n2 - diff);
    }
    else
    {
      // shrink
      // copy all elements into place
      traits_type::move(&curr_data[pos], &curr_data[offset], n2);
      // shift all elements after splice point left
      traits_type::move(&curr_data[pos + n2], &curr_data[pos + n1], curr_size - pos - n1 + 1);
    }
  }
  this->set_size((curr_size - n1) + n2);
  return *this;
}

template<std::size_t N, typename CharT, typename Traits>
template<typename InputIterator>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
replace(
  const_iterator i1,
  const_iterator i2,
  InputIterator j1,
  InputIterator j2) ->
    typename std::enable_if<
      detail::is_input_iterator<
        InputIterator>::value &&
          !detail::is_forward_iterator<
            InputIterator>::value,
              basic_static_string<N, CharT, Traits>&>::type
{
  const auto curr_size = size();
  const auto curr_data = data();
  const std::size_t n1 = detail::distance(i1, i2);
  const std::size_t n2 = read_back(false, j1, j2);
  const std::size_t pos = i1 - curr_data;
  // Rotate to the correct order. [i2, end] will now start with the replaced string,
  // continue to the existing string not being replaced, and end with a null terminator
  std::rotate(&curr_data[pos], &curr_data[curr_size + 1], &curr_data[curr_size + n2 + 1]);
  // Move everything from the end of the splice point to the end of the rotated string to
  // the begining of the splice point
  traits_type::move(&curr_data[pos + n2], &curr_data[pos + n2 + n1], ((curr_size - n1) + n2) - pos);
  this->set_size((curr_size - n1) + n2);
  return *this;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
find(
  const_pointer s,
  size_type pos,
  size_type n) const noexcept ->
    size_type
{
  const auto curr_size = size();
  if (pos > curr_size || n > curr_size - pos)
    return npos;
  if (!n)
    return pos;
  const auto res = detail::search(data() + pos, data() + curr_size, s, s + n, traits_type::eq);
  return res == end() ? npos : detail::distance(data(), res);
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
rfind(
  const_pointer s,
  size_type pos,
  size_type n) const noexcept ->
    size_type
{
  const auto curr_size = size();
  const auto curr_data = data();
  if (curr_size < n)
    return npos;
  if (pos > curr_size - n)
    pos = curr_size - n;
  if (!n)
    return pos;
  for (auto sub = &curr_data[pos]; sub >= curr_data; --sub)
    if (!traits_type::compare(sub, s, n))
      return detail::distance(curr_data, sub);
  return npos;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
find_first_of(
  const_pointer s,
  size_type pos,
  size_type n) const noexcept ->
    size_type
{
  const auto curr_data = data();
  if (pos >= size() || !n)
    return npos;
  const auto res = detail::find_first_of(&curr_data[pos], &curr_data[size()], s, &s[n], traits_type::eq);
  return res == end() ? npos : detail::distance(curr_data, res);
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
find_last_of(
  const_pointer s,
  size_type pos,
  size_type n) const noexcept ->
    size_type
{
  const auto curr_size = size();
  if (!n)
    return npos;
  if (pos >= curr_size)
    pos = 0;
  else
    pos = curr_size - (pos + 1);
  const auto res = detail::find_first_of(rbegin() + pos, rend(), s, &s[n], traits_type::eq);
  return res == rend() ? npos : curr_size - 1 - detail::distance(rbegin(), res);
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
find_first_not_of(
  const_pointer s,
  size_type pos,
  size_type n) const noexcept ->
    size_type
{
  if (pos >= size())
    return npos;
  if (!n)
    return pos;
  const auto res = detail::find_not_of<Traits>(data() + pos, data() + size(), s, n);
  return res == end() ? npos : detail::distance(data(), res);
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
find_last_not_of(
  const_pointer s,
  size_type pos,
  size_type n) const noexcept ->
    size_type
{
  const auto curr_size = size();
  if (pos >= curr_size)
    pos = curr_size - 1;
  if (!n)
    return pos;
  pos = curr_size - (pos + 1);
  const auto res = detail::find_not_of<Traits>(rbegin() + pos, rend(), s, n);
  return res == rend() ? npos : curr_size - 1 - detail::distance(rbegin(), res);
}

template<std::size_t N, typename CharT, typename Traits>
template<typename InputIterator>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
read_back(
  bool overwrite_null,
  InputIterator first,
  InputIterator last) ->
    size_type
{
  const auto curr_data = data();
  auto new_size = size();
  for (; first != last; ++first)
  {
    if (new_size >= max_size())
    {
      // if we overwrote the null terminator,
      // put it back
      if (overwrite_null)
        term();
      detail::throw_exception<std::length_error>(
        "count > max_size() - size()");
    }
    traits_type::assign(curr_data[new_size++ + (!overwrite_null)], *first);
  }
  return new_size - size();
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
replace_unchecked(
  const_iterator i1,
  const_iterator i2,
  const_pointer s,
  size_type n2) ->
    basic_static_string&
{
  const auto curr_data = data();
  const auto curr_size = size();
  const std::size_t pos = i1 - curr_data;
  const std::size_t n1 = i2 - i1;
  if (n2 > max_size() || curr_size - (std::min)(n1, curr_size - pos) >= max_size() - n2)
    detail::throw_exception<std::length_error>(
      "replaced string exceeds max_size()");
  traits_type::move(&curr_data[pos + n2], i2, (end() - i2) + 1);
  traits_type::copy(&curr_data[pos], s, n2);
  this->set_size((curr_size - n1) + n2);
  return *this;
}

template<std::size_t N, typename CharT, typename Traits>
BOOST_STATIC_STRING_CPP14_CONSTEXPR
auto
basic_static_string<N, CharT, Traits>::
insert_unchecked(
  const_iterator pos,
  const_pointer s,
  size_type count) ->
    iterator
{
  const auto curr_data = data();
  const auto curr_size = size();
  if (count > max_size() - curr_size)
    detail::throw_exception<std::length_error>(
      "count > max_size() - curr_size");
  const std::size_t index = pos - curr_data;
  traits_type::move(&curr_data[index + count], pos, (end() - pos) + 1);
  traits_type::copy(&curr_data[index], s, count);
  this->set_size(curr_size + count);
  return curr_data + index;
}

} // static_strings
} // boost

#if defined(__GNUC__) && __GNUC__ >= 7
#pragma GCC diagnostic pop
#endif

#if defined(__GNUC__) && __GNUC__ >= 8
#pragma GCC diagnostic pop
#endif


#endif
#endif
