/*
    This file is part of TON Blockchain Library.
    TON Blockchain Library is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 2 of the License, or
    (at your option) any later version.
    TON Blockchain Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.
    You should have received a copy of the GNU Lesser General Public License
    along with TON Blockchain Library.  If not, see .
    Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include "td/utils/common.h"
#include "td/utils/logging.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
#include "td/utils/StringBuilder.h"
#include 
#include 
#include 
#include 
namespace td {
char *str_dup(Slice str);
template 
std::pair split(T s, char delimiter = ' ') {
  auto delimiter_pos = s.find(delimiter);
  if (delimiter_pos == string::npos) {
    return {std::move(s), T()};
  } else {
    return {s.substr(0, delimiter_pos), s.substr(delimiter_pos + 1)};
  }
}
template 
vector full_split(T s, char delimiter = ' ', size_t max_parts = std::numeric_limits::max()) {
  vector result;
  if (s.empty()) {
    return result;
  }
  while (result.size() + 1 < max_parts) {
    auto delimiter_pos = s.find(delimiter);
    if (delimiter_pos == string::npos) {
      result.push_back(std::move(s));
      return result;
    } else {
      result.push_back(s.substr(0, delimiter_pos));
      s = s.substr(delimiter_pos + 1);
    }
  }
  result.push_back(std::move(s));
  return result;
}
string implode(const vector &v, char delimiter = ' ');
namespace detail {
template 
struct transform_helper {
  template 
  auto transform(const V &v, const Func &f) {
    vector result;
    result.reserve(v.size());
    for (auto &x : v) {
      result.push_back(f(x));
    }
    return result;
  }
  template 
  auto transform(V &&v, const Func &f) {
    vector result;
    result.reserve(v.size());
    for (auto &x : v) {
      result.push_back(f(std::move(x)));
    }
    return result;
  }
};
}  // namespace detail
template 
auto transform(V &&v, const Func &f) {
  return detail::transform_helper>().transform(std::forward(v), f);
}
template 
void remove_if(V &v, const Func &f) {
  size_t i = 0;
  while (i != v.size() && !f(v[i])) {
    i++;
  }
  if (i == v.size()) {
    return;
  }
  size_t j = i;
  while (++i != v.size()) {
    if (!f(v[i])) {
      v[j++] = std::move(v[i]);
    }
  }
  v.erase(v.begin() + j, v.end());
}
template 
bool remove(V &v, const T &value) {
  size_t i = 0;
  while (i != v.size() && v[i] != value) {
    i++;
  }
  if (i == v.size()) {
    return false;
  }
  size_t j = i;
  while (++i != v.size()) {
    if (v[i] != value) {
      v[j++] = std::move(v[i]);
    }
  }
  v.erase(v.begin() + j, v.end());
  return true;
}
template 
bool contains(const V &v, const T &value) {
  for (auto &x : v) {
    if (x == value) {
      return true;
    }
  }
  return false;
}
template 
void reset_to_empty(T &value) {
  using std::swap;
  std::decay_t tmp;
  swap(tmp, value);
}
template 
void append(vector &destination, const vector &source) {
  destination.insert(destination.end(), source.begin(), source.end());
}
template 
void append(vector &destination, vector &&source) {
  if (destination.empty()) {
    destination.swap(source);
    return;
  }
  destination.reserve(destination.size() + source.size());
  for (auto &elem : source) {
    destination.push_back(std::move(elem));
  }
  reset_to_empty(source);
}
template 
void combine(vector &destination, const vector &source) {
  append(destination, source);
}
template 
void combine(vector &destination, vector &&source) {
  if (destination.size() < source.size()) {
    destination.swap(source);
  }
  if (source.empty()) {
    return;
  }
  destination.reserve(destination.size() + source.size());
  for (auto &elem : source) {
    destination.push_back(std::move(elem));
  }
  reset_to_empty(source);
}
inline bool begins_with(Slice str, Slice prefix) {
  return prefix.size() <= str.size() && prefix == Slice(str.data(), prefix.size());
}
inline bool ends_with(Slice str, Slice suffix) {
  return suffix.size() <= str.size() && suffix == Slice(str.data() + str.size() - suffix.size(), suffix.size());
}
inline char to_lower(char c) {
  if ('A' <= c && c <= 'Z') {
    return static_cast(c - 'A' + 'a');
  }
  return c;
}
inline MutableSlice to_lower_inplace(MutableSlice slice) {
  for (auto &c : slice) {
    c = to_lower(c);
  }
  return slice;
}
inline string to_lower(Slice slice) {
  auto result = slice.str();
  to_lower_inplace(result);
  return result;
}
inline char to_upper(char c) {
  if ('a' <= c && c <= 'z') {
    return static_cast(c - 'a' + 'A');
  }
  return c;
}
inline void to_upper_inplace(MutableSlice slice) {
  for (auto &c : slice) {
    c = to_upper(c);
  }
}
inline string to_upper(Slice slice) {
  auto result = slice.str();
  to_upper_inplace(result);
  return result;
}
inline bool is_space(char c) {
  return c == ' ' || c == '\t' || c == '\r' || c == '\n' || c == '\0' || c == '\v';
}
inline bool is_alpha(char c) {
  c |= 0x20;
  return 'a' <= c && c <= 'z';
}
inline bool is_digit(char c) {
  return '0' <= c && c <= '9';
}
inline bool is_alnum(char c) {
  return is_alpha(c) || is_digit(c);
}
inline bool is_hex_digit(char c) {
  if (is_digit(c)) {
    return true;
  }
  c |= 0x20;
  return 'a' <= c && c <= 'f';
}
template 
T trim(T str) {
  auto begin = str.data();
  auto end = begin + str.size();
  while (begin < end && is_space(*begin)) {
    begin++;
  }
  while (begin < end && is_space(end[-1])) {
    end--;
  }
  if (static_cast(end - begin) == str.size()) {
    return std::move(str);
  }
  return T(begin, end);
}
string lpad0(string str, size_t size);
string oneline(Slice str);
template 
std::enable_if_t::value, T> to_integer(Slice str) {
  using unsigned_T = typename std::make_unsigned::type;
  unsigned_T integer_value = 0;
  auto begin = str.begin();
  auto end = str.end();
  bool is_negative = false;
  if (begin != end && *begin == '-') {
    is_negative = true;
    begin++;
  }
  while (begin != end && is_digit(*begin)) {
    integer_value = static_cast(integer_value * 10 + static_cast(*begin++ - '0'));
  }
  if (integer_value > static_cast(std::numeric_limits::max())) {
    static_assert(~0 + 1 == 0, "Two's complement");
    // Use ~x + 1 instead of -x to suppress Visual Studio warning.
    integer_value = static_cast(~integer_value + 1);
    is_negative = !is_negative;
    if (integer_value > static_cast(std::numeric_limits::max())) {
      return std::numeric_limits::min();
    }
  }
  return is_negative ? static_cast(-static_cast(integer_value)) : static_cast(integer_value);
}
template 
std::enable_if_t::value, T> to_integer(Slice str) {
  T integer_value = 0;
  auto begin = str.begin();
  auto end = str.end();
  while (begin != end && is_digit(*begin)) {
    integer_value = static_cast(integer_value * 10 + static_cast(*begin++ - '0'));
  }
  return integer_value;
}
template 
Result to_integer_safe(Slice str) {
  auto res = to_integer(str);
  if ((PSLICE() << res) != str) {
    return Status::Error(PSLICE() << "Can't parse \"" << str << "\" as number");
  }
  return res;
}
inline int hex_to_int(char c) {
  if (is_digit(c)) {
    return c - '0';
  }
  c |= 0x20;
  if ('a' <= c && c <= 'f') {
    return c - 'a' + 10;
  }
  return 16;
}
template 
typename std::enable_if::value, T>::type hex_to_integer(Slice str) {
  T integer_value = 0;
  auto begin = str.begin();
  auto end = str.end();
  while (begin != end && is_hex_digit(*begin)) {
    integer_value = static_cast(integer_value * 16 + hex_to_int(*begin++));
  }
  return integer_value;
}
template 
Result::value, T>::type> hex_to_integer_safe(Slice str) {
  T integer_value = 0;
  auto begin = str.begin();
  auto end = str.end();
  while (begin != end) {
    if (!is_hex_digit(*begin)) {
      return Status::Error("not a hex digit");
    }
    integer_value = static_cast(integer_value * 16 + hex_to_int(*begin++));
  }
  return integer_value;
}
double to_double(Slice str);
template 
T clamp(T value, T min_value, T max_value) {
  if (value < min_value) {
    return min_value;
  }
  if (value > max_value) {
    return max_value;
  }
  return value;
}
Result hex_decode(Slice hex);
string hex_encode(Slice data);
string url_encode(Slice data);
// run-time checked narrowing cast (type conversion):
namespace detail {
template 
struct is_same_signedness : public std::integral_constant::value == std::is_signed::value> {
};
template 
struct safe_undeflying_type {
  using type = T;
};
template 
struct safe_undeflying_type::value>> {
  using type = std::underlying_type_t;
};
class NarrowCast {
  const char *file_;
  int line_;
 public:
  NarrowCast(const char *file, int line) : file_(file), line_(line) {
  }
  template 
  R cast(const A &a) {
    using RT = typename safe_undeflying_type::type;
    using AT = typename safe_undeflying_type::type;
    static_assert(std::is_integral