//   Copyright (c) 2018 PaddlePaddle Authors. All Rights Reserve.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// tinyformat.h
// Copyright (C) 2011, Chris Foster [chris42f (at) gmail (d0t) com]
//
// Boost Software License - Version 1.0
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

//------------------------------------------------------------------------------
// Tinyformat: A minimal type safe printf replacement
//
// tinyformat.h is a type safe printf replacement library in a single C++
// header file.  Design goals include:
//
// * Type safety and extensibility for user defined types.
// * C99 printf() compatibility, to the extent possible using std::ostream
// * Simplicity and minimalism.  A single header file to include and distribute
//   with your projects.
// * Augment rather than replace the standard stream formatting mechanism
// * C++98 support, with optional C++11 niceties
//
//
// Main interface example usage
// ----------------------------
//
// To print a date to std::cout:
//
//   std::string weekday = "Wednesday";
//   const char* month = "July";
//   size_t day = 27;
//   long hour = 14;
//   int min = 44;
//
//   tfm::printf("%s, %s %d, %.2d:%.2d\n", weekday, month, day, hour, min);
//
// The strange types here emphasize the type safety of the interface; it is
// possible to print a std::string using the "%s" conversion, and a
// size_t using the "%d" conversion.  A similar result could be achieved
// using either of the tfm::format() functions.  One prints on a user provided
// stream:
//
//   tfm::format(std::cerr, "%s, %s %d, %.2d:%.2d\n",
//               weekday, month, day, hour, min);
//
// The other returns a std::string:
//
//   std::string date = tfm::format("%s, %s %d, %.2d:%.2d\n",
//                                  weekday, month, day, hour, min);
//   std::cout << date;
//
// These are the three primary interface functions.  There is also a
// convenience function printfln() which appends a newline to the usual result
// of printf() for super simple logging.
//
//
// User defined format functions
// -----------------------------
//
// Simulating variadic templates in C++98 is pretty painful since it requires
// writing out the same function for each desired number of arguments.  To make
// this bearable tinyformat comes with a set of macros which are used
// internally to generate the API, but which may also be used in user code.
//
// The three macros TINYFORMAT_ARGTYPES(n), TINYFORMAT_VARARGS(n) and
// TINYFORMAT_PASSARGS(n) will generate a list of n argument types,
// type/name pairs and argument names respectively when called with an integer
// n between 1 and 16.  We can use these to define a macro which generates the
// desired user defined function with n arguments.  To generate all 16 user
// defined function bodies, use the macro TINYFORMAT_FOREACH_ARGNUM.  For an
// example, see the implementation of printf() at the end of the source file.
//
// Sometimes it's useful to be able to pass a list of format arguments through
// to a non-template function.  The FormatList class is provided as a way to do
// this by storing the argument list in a type-opaque way.  Continuing the
// example from above, we construct a FormatList using makeFormatList():
//
//   FormatListRef formatList = tfm::makeFormatList(weekday, month, day, hour,
//   min);
//
// The format list can now be passed into any non-template function and used
// via a call to the vformat() function:
//
//   tfm::vformat(std::cout, "%s, %s %d, %.2d:%.2d\n", formatList);
//
//
// Additional API information
// --------------------------
//
// Error handling: Define TINYFORMAT_ERROR to customize the error handling for
// format strings which are unsupported or have the wrong number of format
// specifiers (calls assert() by default).
//
// User defined types: Uses operator<< for user defined types by default.
// Overload formatValue() for more control.

#pragma once

#include <algorithm>
#include <cassert>
#include <iostream>
#include <sstream>

namespace paddle {
namespace string {
namespace tinyformat {

#ifndef TINYFORMAT_ERROR
#define TINYFORMAT_ERROR(reason) assert(0 && reason)
#endif

//------------------------------------------------------------------------------
namespace detail {

// Test whether type T1 is convertible to type T2
template <typename T1, typename T2>
struct is_convertible {
 private:
  // two types of different size
  struct fail {
    char dummy[2];
  };
  struct succeed {
    char dummy;
  };
  // Try to convert a T1 to a T2 by plugging into tryConvert
  static fail tryConvert(...);
  static succeed tryConvert(const T2 &);
  static const T1 &makeT1();

 public:
  // Standard trick: the (...) version of tryConvert will be chosen from
  // the overload set only if the version taking a T2 doesn't match.
  // Then we compare the sizes of the return types to check which
  // function matched.  Very neat, in a disgusting kind of way :)
  static const bool value = sizeof(tryConvert(makeT1())) == sizeof(succeed);
};

// Format the value by casting to type fmtT.  This default implementation
// should never be called.
template <typename T, typename fmtT,
          bool convertible = is_convertible<T, fmtT>::value>
struct formatValueAsType {
  static void invoke(std::ostream & /*out*/, const T & /*value*/) { assert(0); }
};
// Specialized version for types that can actually be converted to fmtT, as
// indicated by the "convertible" template parameter.
template <typename T, typename fmtT>
struct formatValueAsType<T, fmtT, true> {
  static void invoke(std::ostream &out, const T &value) {
    out << static_cast<fmtT>(value);
  }
};

// Convert an arbitrary type to integer.  The version with convertible=false
// throws an error.
template <typename T, bool convertible = is_convertible<T, int>::value>
struct convertToInt {
  static int invoke(const T & /*value*/) {
    TINYFORMAT_ERROR(
        "tinyformat: Cannot convert from argument type to "
        "integer for use as variable width or precision");
    return 0;
  }
};
// Specialization for convertToInt when conversion is possible
template <typename T>
struct convertToInt<T, true> {
  static int invoke(const T &value) { return static_cast<int>(value); }
};

// Format at most ntrunc characters to the given stream.
template <typename T>
inline void formatTruncated(std::ostream &out, const T &value, int ntrunc) {
  std::ostringstream tmp;
  tmp << value;
  std::string result = tmp.str();
  out.write(result.c_str(),
            (std::min)(ntrunc, static_cast<int>(result.size())));
}
#define TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(type)                       \
  inline void formatTruncated(std::ostream &out, type *value, int ntrunc) { \
    std::streamsize len = 0;                                                \
    while (len < ntrunc && value[len] != 0) ++len;                          \
    out.write(value, len);                                                  \
  }
// Overload for const char* and char*.  Could overload for signed & unsigned
// char too, but these are technically unneeded for printf compatibility.
TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(const char)
TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR(char)
#undef TINYFORMAT_DEFINE_FORMAT_TRUNCATED_CSTR

}  // namespace detail

//------------------------------------------------------------------------------
// Variable formatting functions.  May be overridden for user-defined types if
// desired.

/// Format a value into a stream, delegating to operator<< by default.
///
/// Users may override this for their own types.  When this function is called,
/// the stream flags will have been modified according to the format string.
/// The format specification is provided in the range [fmtBegin, fmtEnd).  For
/// truncating conversions, ntrunc is set to the desired maximum number of
/// characters, for example "%.7s" calls formatValue with ntrunc = 7.
///
/// By default, formatValue() uses the usual stream insertion operator
/// operator<< to format the type T, with special cases for the %c and %p
/// conversions.
template <typename T>
inline void formatValue(std::ostream &out, const char * /*fmtBegin*/,
                        const char *fmtEnd, int ntrunc, const T &value) {
  // The mess here is to support the %c and %p conversions: if these
  // conversions are active we try to convert the type to a char or const
  // void* respectively and format that instead of the value itself.  For the
  // %p conversion it's important to avoid dereferencing the pointer, which
  // could otherwise lead to a crash when printing a dangling (const char*).
  const bool canConvertToChar = detail::is_convertible<T, char>::value;
  const bool canConvertToVoidPtr =
      detail::is_convertible<T, const void *>::value;
  if (canConvertToChar && *(fmtEnd - 1) == 'c')
    detail::formatValueAsType<T, char>::invoke(out, value);
  else if (canConvertToVoidPtr && *(fmtEnd - 1) == 'p')
    detail::formatValueAsType<T, const void *>::invoke(out, value);
  else if (ntrunc >= 0) {
    // Take care not to overread C strings in truncating conversions like
    // "%.4s" where at most 4 characters may be read.
    detail::formatTruncated(out, value, ntrunc);
  } else
    out << value;
}

// Overloaded version for char types to support printing as an integer
#define TINYFORMAT_DEFINE_FORMATVALUE_CHAR(charType)                      \
  inline void formatValue(std::ostream &out, const char * /*fmtBegin*/,   \
                          const char *fmtEnd, int /**/, charType value) { \
    switch (*(fmtEnd - 1)) {                                              \
      case 'u':                                                           \
      case 'd':                                                           \
      case 'i':                                                           \
      case 'o':                                                           \
      case 'X':                                                           \
      case 'x':                                                           \
        out << static_cast<int>(value);                                   \
        break;                                                            \
      default:                                                            \
        out << value;                                                     \
        break;                                                            \
    }                                                                     \
  }
// per 3.9.1: char, signed char and unsigned char are all distinct types
TINYFORMAT_DEFINE_FORMATVALUE_CHAR(char)
TINYFORMAT_DEFINE_FORMATVALUE_CHAR(signed char)
TINYFORMAT_DEFINE_FORMATVALUE_CHAR(unsigned char)
#undef TINYFORMAT_DEFINE_FORMATVALUE_CHAR

//------------------------------------------------------------------------------
// Tools for emulating variadic templates in C++98.  The basic idea here is
// stolen from the boost preprocessor metaprogramming library and cut down to
// be just general enough for what we need.

#define TINYFORMAT_ARGTYPES(n) TINYFORMAT_ARGTYPES_##n
#define TINYFORMAT_VARARGS(n) TINYFORMAT_VARARGS_##n
#define TINYFORMAT_PASSARGS(n) TINYFORMAT_PASSARGS_##n
#define TINYFORMAT_PASSARGS_TAIL(n) TINYFORMAT_PASSARGS_TAIL_##n

// To keep it as transparent as possible, the macros below have been generated
// using python via the excellent cog.py code generation script.  This avoids
// the need for a bunch of complex (but more general) preprocessor tricks as
// used in boost.preprocessor.
//
// To rerun the code generation in place, use `cog.py -r tinyformat.h`
// (see http://nedbatchelder.com/code/cog).  Alternatively you can just create
// extra versions by hand.

/*[[[cog
maxParams = 16

def makeCommaSepLists(lineTemplate, elemTemplate, startInd=1):
    for j in range(startInd,maxParams+1):
        list = ', '.join([elemTemplate % {'i':i} for i in range(startInd,j+1)])
        cog.outl(lineTemplate % {'j':j, 'list':list})

makeCommaSepLists('#define TINYFORMAT_ARGTYPES_%(j)d %(list)s',
                  'class T%(i)d')

cog.outl()
makeCommaSepLists('#define TINYFORMAT_VARARGS_%(j)d %(list)s',
                  'const T%(i)d& v%(i)d')

cog.outl()
makeCommaSepLists('#define TINYFORMAT_PASSARGS_%(j)d %(list)s', 'v%(i)d')

cog.outl()
cog.outl('#define TINYFORMAT_PASSARGS_TAIL_1')
makeCommaSepLists('#define TINYFORMAT_PASSARGS_TAIL_%(j)d , %(list)s',
                  'v%(i)d', startInd = 2)

cog.outl()
cog.outl('#define TINYFORMAT_FOREACH_ARGNUM(m) \\\n    ' +
         ' '.join(['m(%d)' % (j,) for j in range(1,maxParams+1)]))
]]]*/
#define TINYFORMAT_ARGTYPES_1 class T1
#define TINYFORMAT_ARGTYPES_2 class T1, class T2
#define TINYFORMAT_ARGTYPES_3 class T1, class T2, class T3
#define TINYFORMAT_ARGTYPES_4 class T1, class T2, class T3, class T4
#define TINYFORMAT_ARGTYPES_5 class T1, class T2, class T3, class T4, class T5
#define TINYFORMAT_ARGTYPES_6 \
  class T1, class T2, class T3, class T4, class T5, class T6
#define TINYFORMAT_ARGTYPES_7 \
  class T1, class T2, class T3, class T4, class T5, class T6, class T7
#define TINYFORMAT_ARGTYPES_8 \
  class T1, class T2, class T3, class T4, class T5, class T6, class T7, class T8
#define TINYFORMAT_ARGTYPES_9                                           \
  class T1, class T2, class T3, class T4, class T5, class T6, class T7, \
      class T8, class T9
#define TINYFORMAT_ARGTYPES_10                                          \
  class T1, class T2, class T3, class T4, class T5, class T6, class T7, \
      class T8, class T9, class T10
#define TINYFORMAT_ARGTYPES_11                                          \
  class T1, class T2, class T3, class T4, class T5, class T6, class T7, \
      class T8, class T9, class T10, class T11
#define TINYFORMAT_ARGTYPES_12                                          \
  class T1, class T2, class T3, class T4, class T5, class T6, class T7, \
      class T8, class T9, class T10, class T11, class T12
#define TINYFORMAT_ARGTYPES_13                                          \
  class T1, class T2, class T3, class T4, class T5, class T6, class T7, \
      class T8, class T9, class T10, class T11, class T12, class T13
#define TINYFORMAT_ARGTYPES_14                                          \
  class T1, class T2, class T3, class T4, class T5, class T6, class T7, \
      class T8, class T9, class T10, class T11, class T12, class T13,   \
      class T14
#define TINYFORMAT_ARGTYPES_15                                          \
  class T1, class T2, class T3, class T4, class T5, class T6, class T7, \
      class T8, class T9, class T10, class T11, class T12, class T13,   \
      class T14, class T15
#define TINYFORMAT_ARGTYPES_16                                          \
  class T1, class T2, class T3, class T4, class T5, class T6, class T7, \
      class T8, class T9, class T10, class T11, class T12, class T13,   \
      class T14, class T15, class T16

#define TINYFORMAT_VARARGS_1 const T1 &v1
#define TINYFORMAT_VARARGS_2 const T1 &v1, const T2 &v2
#define TINYFORMAT_VARARGS_3 const T1 &v1, const T2 &v2, const T3 &v3
#define TINYFORMAT_VARARGS_4 \
  const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4
#define TINYFORMAT_VARARGS_5 \
  const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5
#define TINYFORMAT_VARARGS_6                                            \
  const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, \
      const T6 &v6
#define TINYFORMAT_VARARGS_7                                            \
  const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, \
      const T6 &v6, const T7 &v7
#define TINYFORMAT_VARARGS_8                                            \
  const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, \
      const T6 &v6, const T7 &v7, const T8 &v8
#define TINYFORMAT_VARARGS_9                                            \
  const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, \
      const T6 &v6, const T7 &v7, const T8 &v8, const T9 &v9
#define TINYFORMAT_VARARGS_10                                           \
  const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5, \
      const T6 &v6, const T7 &v7, const T8 &v8, const T9 &v9, const T10 &v10
#define TINYFORMAT_VARARGS_11                                                 \
  const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5,       \
      const T6 &v6, const T7 &v7, const T8 &v8, const T9 &v9, const T10 &v10, \
      const T11 &v11
#define TINYFORMAT_VARARGS_12                                                 \
  const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5,       \
      const T6 &v6, const T7 &v7, const T8 &v8, const T9 &v9, const T10 &v10, \
      const T11 &v11, const T12 &v12
#define TINYFORMAT_VARARGS_13                                                 \
  const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5,       \
      const T6 &v6, const T7 &v7, const T8 &v8, const T9 &v9, const T10 &v10, \
      const T11 &v11, const T12 &v12, const T13 &v13
#define TINYFORMAT_VARARGS_14                                                 \
  const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5,       \
      const T6 &v6, const T7 &v7, const T8 &v8, const T9 &v9, const T10 &v10, \
      const T11 &v11, const T12 &v12, const T13 &v13, const T14 &v14
#define TINYFORMAT_VARARGS_15                                                 \
  const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5,       \
      const T6 &v6, const T7 &v7, const T8 &v8, const T9 &v9, const T10 &v10, \
      const T11 &v11, const T12 &v12, const T13 &v13, const T14 &v14,         \
      const T15 &v15
#define TINYFORMAT_VARARGS_16                                                 \
  const T1 &v1, const T2 &v2, const T3 &v3, const T4 &v4, const T5 &v5,       \
      const T6 &v6, const T7 &v7, const T8 &v8, const T9 &v9, const T10 &v10, \
      const T11 &v11, const T12 &v12, const T13 &v13, const T14 &v14,         \
      const T15 &v15, const T16 &v16

#define TINYFORMAT_PASSARGS_1 v1
#define TINYFORMAT_PASSARGS_2 v1, v2
#define TINYFORMAT_PASSARGS_3 v1, v2, v3
#define TINYFORMAT_PASSARGS_4 v1, v2, v3, v4
#define TINYFORMAT_PASSARGS_5 v1, v2, v3, v4, v5
#define TINYFORMAT_PASSARGS_6 v1, v2, v3, v4, v5, v6
#define TINYFORMAT_PASSARGS_7 v1, v2, v3, v4, v5, v6, v7
#define TINYFORMAT_PASSARGS_8 v1, v2, v3, v4, v5, v6, v7, v8
#define TINYFORMAT_PASSARGS_9 v1, v2, v3, v4, v5, v6, v7, v8, v9
#define TINYFORMAT_PASSARGS_10 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10
#define TINYFORMAT_PASSARGS_11 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11
#define TINYFORMAT_PASSARGS_12 v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12
#define TINYFORMAT_PASSARGS_13 \
  v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13
#define TINYFORMAT_PASSARGS_14 \
  v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14
#define TINYFORMAT_PASSARGS_15 \
  v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15
#define TINYFORMAT_PASSARGS_16 \
  v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16

#define TINYFORMAT_PASSARGS_TAIL_1
#define TINYFORMAT_PASSARGS_TAIL_2 , v2
#define TINYFORMAT_PASSARGS_TAIL_3 , v2, v3
#define TINYFORMAT_PASSARGS_TAIL_4 , v2, v3, v4
#define TINYFORMAT_PASSARGS_TAIL_5 , v2, v3, v4, v5
#define TINYFORMAT_PASSARGS_TAIL_6 , v2, v3, v4, v5, v6
#define TINYFORMAT_PASSARGS_TAIL_7 , v2, v3, v4, v5, v6, v7
#define TINYFORMAT_PASSARGS_TAIL_8 , v2, v3, v4, v5, v6, v7, v8
#define TINYFORMAT_PASSARGS_TAIL_9 , v2, v3, v4, v5, v6, v7, v8, v9
#define TINYFORMAT_PASSARGS_TAIL_10 , v2, v3, v4, v5, v6, v7, v8, v9, v10
#define TINYFORMAT_PASSARGS_TAIL_11 , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11
#define TINYFORMAT_PASSARGS_TAIL_12 \
  , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12
#define TINYFORMAT_PASSARGS_TAIL_13 \
  , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13
#define TINYFORMAT_PASSARGS_TAIL_14 \
  , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14
#define TINYFORMAT_PASSARGS_TAIL_15 \
  , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15
#define TINYFORMAT_PASSARGS_TAIL_16 \
  , v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16

#define TINYFORMAT_FOREACH_ARGNUM(m)                                         \
  m(1) m(2) m(3) m(4) m(5) m(6) m(7) m(8) m(9) m(10) m(11) m(12) m(13) m(14) \
      m(15) m(16)
//[[[end]]]

namespace detail {

// Type-opaque holder for an argument to format(), with associated actions on
// the type held as explicit function pointers.  This allows FormatArg's for
// each argument to be allocated as a homogenous array inside FormatList
// whereas a naive implementation based on inheritance does not.
class FormatArg {
 public:
  FormatArg() {}

  template <typename T>
  FormatArg(const T &value)
      : m_value(static_cast<const void *>(&value)),
        m_formatImpl(&formatImpl<T>),
        m_toIntImpl(&toIntImpl<T>) {}

  void format(std::ostream &out, const char *fmtBegin, const char *fmtEnd,
              int ntrunc) const {
    m_formatImpl(out, fmtBegin, fmtEnd, ntrunc, m_value);
  }

  int toInt() const { return m_toIntImpl(m_value); }

 private:
  template <typename T>
  static void formatImpl(std::ostream &out, const char *fmtBegin,
                         const char *fmtEnd, int ntrunc, const void *value) {
    formatValue(out, fmtBegin, fmtEnd, ntrunc, *static_cast<const T *>(value));
  }

  template <typename T>
  static int toIntImpl(const void *value) {
    return convertToInt<T>::invoke(*static_cast<const T *>(value));
  }

  const void *m_value;
  void (*m_formatImpl)(std::ostream &out, const char *fmtBegin,
                       const char *fmtEnd, int ntrunc, const void *value);
  int (*m_toIntImpl)(const void *value);
};

// Parse and return an integer from the string c, as atoi()
// On return, c is set to one past the end of the integer.
inline int parseIntAndAdvance(const char *&c) {
  int i = 0;
  for (; *c >= '0' && *c <= '9'; ++c) i = 10 * i + (*c - '0');
  return i;
}

// Print literal part of format string and return next format spec
// position.
//
// Skips over any occurrences of '%%', printing a literal '%' to the
// output.  The position of the first % character of the next
// nontrivial format spec is returned, or the end of string.
inline const char *printFormatStringLiteral(std::ostream &out,
                                            const char *fmt) {
  const char *c = fmt;
  for (;; ++c) {
    switch (*c) {
      case '\0':
        out.write(fmt, c - fmt);
        return c;
      case '%':
        out.write(fmt, c - fmt);
        if (*(c + 1) != '%') return c;
        // for "%%", tack trailing % onto next literal section.
        fmt = ++c;
        break;
      default:
        break;
    }
  }
}

// Parse a format string and set the stream state accordingly.
//
// The format mini-language recognized here is meant to be the one from C99,
// with the form "%[flags][width][.precision][length]type".
//
// Formatting options which can't be natively represented using the ostream
// state are returned in spacePadPositive (for space padded positive numbers)
// and ntrunc (for truncating conversions).  argIndex is incremented if
// necessary to pull out variable width and precision .  The function returns a
// pointer to the character after the end of the current format spec.
inline const char *streamStateFromFormat(std::ostream &out,
                                         bool &spacePadPositive, int &ntrunc,
                                         const char *fmtStart,
                                         const detail::FormatArg *formatters,
                                         int &argIndex, int numFormatters) {
  if (*fmtStart != '%') {
    TINYFORMAT_ERROR(
        "tinyformat: Not enough conversion specifiers in format string");
    return fmtStart;
  }
  // Reset stream state to defaults.
  out.width(0);
  out.precision(6);
  out.fill(' ');
  // Reset most flags; ignore irrelevant unitbuf & skipws.
  out.unsetf(std::ios::adjustfield | std::ios::basefield |
             std::ios::floatfield | std::ios::showbase | std::ios::boolalpha |
             std::ios::showpoint | std::ios::showpos | std::ios::uppercase);
  bool precisionSet = false;
  bool widthSet = false;
  int widthExtra = 0;
  const char *c = fmtStart + 1;
  // 1) Parse flags
  for (;; ++c) {
    switch (*c) {
      case '#':
        out.setf(std::ios::showpoint | std::ios::showbase);
        continue;
      case '0':
        // overridden by left alignment ('-' flag)
        if (!(out.flags() & std::ios::left)) {
          // Use internal padding so that numeric values are
          // formatted correctly, eg -00010 rather than 000-10
          out.fill('0');
          out.setf(std::ios::internal, std::ios::adjustfield);
        }
        continue;
      case '-':
        out.fill(' ');
        out.setf(std::ios::left, std::ios::adjustfield);
        continue;
      case ' ':
        // overridden by show positive sign, '+' flag.
        if (!(out.flags() & std::ios::showpos)) spacePadPositive = true;
        continue;
      case '+':
        out.setf(std::ios::showpos);
        spacePadPositive = false;
        widthExtra = 1;
        continue;
      default:
        break;
    }
    break;
  }
  // 2) Parse width
  if (*c >= '0' && *c <= '9') {
    widthSet = true;
    out.width(parseIntAndAdvance(c));
  }
  if (*c == '*') {
    widthSet = true;
    int width = 0;
    if (argIndex < numFormatters)
      width = formatters[argIndex++].toInt();
    else
      TINYFORMAT_ERROR(
          "tinyformat: Not enough arguments to read variable width");
    if (width < 0) {
      // negative widths correspond to '-' flag set
      out.fill(' ');
      out.setf(std::ios::left, std::ios::adjustfield);
      width = -width;
    }
    out.width(width);
    ++c;
  }
  // 3) Parse precision
  if (*c == '.') {
    ++c;
    int precision = 0;
    if (*c == '*') {
      ++c;
      if (argIndex < numFormatters)
        precision = formatters[argIndex++].toInt();
      else
        TINYFORMAT_ERROR(
            "tinyformat: Not enough arguments to read variable precision");
    } else {
      if (*c >= '0' && *c <= '9')
        precision = parseIntAndAdvance(c);
      else if (*c == '-')  // negative precisions ignored, treated as zero.
        parseIntAndAdvance(++c);
    }
    out.precision(precision);
    precisionSet = true;
  }
  // 4) Ignore any C99 length modifier
  while (*c == 'l' || *c == 'h' || *c == 'L' || *c == 'j' || *c == 'z' ||
         *c == 't')
    ++c;
  // 5) We're up to the conversion specifier character.
  // Set stream flags based on conversion specifier (thanks to the
  // boost::format class for forging the way here).
  bool intConversion = false;
  switch (*c) {
    case 'u':
    case 'd':
    case 'i':
      out.setf(std::ios::dec, std::ios::basefield);
      intConversion = true;
      break;
    case 'o':
      out.setf(std::ios::oct, std::ios::basefield);
      intConversion = true;
      break;
    case 'X':
      out.setf(std::ios::uppercase);
    case 'x':
    case 'p':
      out.setf(std::ios::hex, std::ios::basefield);
      intConversion = true;
      break;
    case 'E':
      out.setf(std::ios::uppercase);
    case 'e':
      out.setf(std::ios::scientific, std::ios::floatfield);
      out.setf(std::ios::dec, std::ios::basefield);
      break;
    case 'F':
      out.setf(std::ios::uppercase);
    case 'f':
      out.setf(std::ios::fixed, std::ios::floatfield);
      break;
    case 'G':
      out.setf(std::ios::uppercase);
    case 'g':
      out.setf(std::ios::dec, std::ios::basefield);
      // As in boost::format, let stream decide float format.
      out.flags(out.flags() & ~std::ios::floatfield);
      break;
    case 'a':
    case 'A':
      TINYFORMAT_ERROR(
          "tinyformat: the %a and %A conversion specs "
          "are not supported");
      break;
    case 'c':
      // Handled as special case inside formatValue()
      break;
    case 's':
      if (precisionSet) ntrunc = static_cast<int>(out.precision());
      // Make %s print booleans as "true" and "false"
      out.setf(std::ios::boolalpha);
      break;
    case 'n':
      // Not supported - will cause problems!
      TINYFORMAT_ERROR("tinyformat: %n conversion spec not supported");
      break;
    case '\0':
      TINYFORMAT_ERROR(
          "tinyformat: Conversion spec incorrectly "
          "terminated by end of string");
      return c;
    default:
      break;
  }
  if (intConversion && precisionSet && !widthSet) {
    // "precision" for integers gives the minimum number of digits (to be
    // padded with zeros on the left).  This isn't really supported by the
    // iostreams, but we can approximately simulate it with the width if
    // the width isn't otherwise used.
    out.width(out.precision() + widthExtra);
    out.setf(std::ios::internal, std::ios::adjustfield);
    out.fill('0');
  }
  return c + 1;
}

//------------------------------------------------------------------------------
inline void formatImpl(std::ostream &out, const char *fmt,
                       const detail::FormatArg *formatters, int numFormatters) {
  // Saved stream state
  std::streamsize origWidth = out.width();
  std::streamsize origPrecision = out.precision();
  std::ios::fmtflags origFlags = out.flags();
  char origFill = out.fill();

  for (int argIndex = 0; argIndex < numFormatters; ++argIndex) {
    // Parse the format string
    fmt = printFormatStringLiteral(out, fmt);
    bool spacePadPositive = false;
    int ntrunc = -1;
    const char *fmtEnd =
        streamStateFromFormat(out, spacePadPositive, ntrunc, fmt, formatters,
                              argIndex, numFormatters);
    if (argIndex >= numFormatters) {
      // Check args remain after reading any variable width/precision
      TINYFORMAT_ERROR("tinyformat: Not enough format arguments");
      return;
    }
    const FormatArg &arg = formatters[argIndex];
    // Format the arg into the stream.
    if (!spacePadPositive)
      arg.format(out, fmt, fmtEnd, ntrunc);
    else {
      // The following is a special case with no direct correspondence
      // between stream formatting and the printf() behaviour.  Simulate
      // it crudely by formatting into a temporary string stream and
      // munging the resulting string.
      std::ostringstream tmpStream;
      tmpStream.copyfmt(out);
      tmpStream.setf(std::ios::showpos);
      arg.format(tmpStream, fmt, fmtEnd, ntrunc);
      std::string result = tmpStream.str();  // allocates... yuck.
      for (size_t i = 0, iend = result.size(); i < iend; ++i)
        if (result[i] == '+') result[i] = ' ';
      out << result;
    }
    fmt = fmtEnd;
  }

  // Print remaining part of format string.
  fmt = printFormatStringLiteral(out, fmt);
  if (*fmt != '\0')
    TINYFORMAT_ERROR(
        "tinyformat: Too many conversion specifiers in format string");

  // Restore stream state
  out.width(origWidth);
  out.precision(origPrecision);
  out.flags(origFlags);
  out.fill(origFill);
}

}  // namespace detail

/// List of template arguments format(), held in a type-opaque way.
///
/// A const reference to FormatList (typedef'd as FormatListRef) may be
/// conveniently used to pass arguments to non-template functions: All type
/// information has been stripped from the arguments, leaving just enough of a
/// common interface to perform formatting as required.
class FormatList {
 public:
  FormatList(detail::FormatArg *formatters, int N)
      : m_formatters(formatters), m_N(N) {}

  friend void vformat(std::ostream &out, const char *fmt,
                      const FormatList &list);

 private:
  const detail::FormatArg *m_formatters;
  int m_N;
};

/// Reference to type-opaque format list for passing to vformat()
typedef const FormatList &FormatListRef;

namespace detail {

// Format list subclass with fixed storage to avoid dynamic allocation
template <int N>
class FormatListN : public FormatList {
 public:
  template <typename... Args>
  FormatListN(const Args &... args)
      : FormatList(&m_formatterStore[0], N),
        m_formatterStore{FormatArg(args)...} {
    static_assert(sizeof...(args) == N, "Number of args must be N");
  }

 private:
  FormatArg m_formatterStore[N];
};

// Special 0-arg version - MSVC says zero-sized C array in struct is nonstandard
template <>
class FormatListN<0> : public FormatList {
 public:
  FormatListN() : FormatList(0, 0) {}
};

}  // namespace detail

//------------------------------------------------------------------------------
// Primary API functions

/// Make type-agnostic format list from list of template arguments.
///
/// The exact return type of this function is an implementation detail and
/// shouldn't be relied upon.  Instead it should be stored as a FormatListRef:
///
///   FormatListRef formatList = makeFormatList( /*...*/ );
template <typename... Args>
detail::FormatListN<sizeof...(Args)> makeFormatList(const Args &... args) {
  return detail::FormatListN<sizeof...(args)>(args...);
}

/// Format list of arguments to the stream according to the given format string.
///
/// The name vformat() is chosen for the semantic similarity to vprintf(): the
/// list of format arguments is held in a single function argument.
inline void vformat(std::ostream &out, const char *fmt, FormatListRef list) {
  detail::formatImpl(out, fmt, list.m_formatters, list.m_N);
}

/// Format list of arguments to the stream according to given format string.
template <typename... Args>
void format(std::ostream &out, const char *fmt, const Args &... args) {
  vformat(out, fmt, makeFormatList(args...));
}

/// Format list of arguments according to the given format string and return
/// the result as a string.
template <typename... Args>
std::string format(const char *fmt, const Args &... args) {
  std::ostringstream oss;
  format(oss, fmt, args...);
  return oss.str();
}

/// Format list of arguments to std::cout, according to the given format string
template <typename... Args>
void printf(const char *fmt, const Args &... args) {
  format(std::cout, fmt, args...);
}

template <typename... Args>
void printfln(const char *fmt, const Args &... args) {
  format(std::cout, fmt, args...);
  std::cout << '\n';
}

}  // namespace tinyformat
}  // namespace string
}  // namespace paddle
