text: # -*- coding: utf-8 -*-
  # ==============================================================================
  # MIT License
  #
  # Copyright (c) 2020 Albert Moky
  #
  # Permission is hereby granted, free of charge, to any person obtaining a copy
  # of this software and associated documentation files (the "Software"), to deal
  # in the Software without restriction, including without limitation the rights
  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  # copies of the Software, and to permit persons to whom the Software is
  # furnished to do so, subject to the following conditions:
  #
  # The above copyright notice and this permission notice shall be included in all
  # copies or substantial portions of the Software.
  #
  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  # SOFTWARE.
  # ==============================================================================
  
  from typing import Optional, Union, Any, Tuple, Mapping, List, Iterable, Iterator
  
  
  class String:
      """
          A container with inner string
      """
  
      @classmethod
      def unwrap(cls, s: str) -> str:
          # unwrap string container
          if isinstance(s, String):
              return s.string
          # assert isinstance(s, str)
          return s
  
      # def __new__(cls, string: Optional[str]=None):
      #     return super().__new__(cls, string=string)
  
      def __init__(self, string: Optional[str] = None):
          super().__init__()
          if string is None:
              self.__string = ''
          elif isinstance(string, String):
              self.__string = string.string
          else:
              assert isinstance(string, str), 'string error: %s' % string
              self.__string = string
  
      @property
      def string(self) -> str:
          return self.__string
  
      def capitalize(self):
          """
          Return a capitalized version of the string.
  
          More specifically, make the first character have upper case and the rest lower
          case.
          """
          string = self.__string.capitalize()
          return String(string=string)
  
      def casefold(self):
          """ Return a version of the string suitable for caseless comparisons. """
          string = self.__string.casefold()
          return String(string=string)
  
      def center(self, width: int, fillchar: str = ' '):
          """
          Return a centered string of length width.
  
          Padding is done using the specified fill character (default is a space).
          """
          if isinstance(fillchar, String):
              fillchar = fillchar.string
          string = self.__string.center(width, fillchar)
          return String(string=string)
  
      def count(self, x: str, __start: Optional[int] = None, __end: Optional[int] = None) -> int:
          """
          S.count(sub[, start[, end]]) -> int
  
          Return the number of non-overlapping occurrences of substring sub in
          string S[start:end].  Optional arguments start and end are
          interpreted as in slice notation.
          """
          if isinstance(x, String):
              x = x.string
          return self.__string.count(x, __start, __end)
  
      def encode(self, encoding: str = 'utf-8', errors: str = 'strict') -> bytes:
          """
          Encode the string using the codec registered for encoding.
  
            encoding
              The encoding in which to encode the string.
            errors
              The error handling scheme to use for encoding errors.
              The default is 'strict' meaning that encoding errors raise a
              UnicodeEncodeError.  Other possible values are 'ignore', 'replace' and
              'xmlcharrefreplace' as well as any other name registered with
              codecs.register_error that can handle UnicodeEncodeErrors.
          """
          return self.__string.encode(encoding, errors)
  
      def endswith(self, suffix: str, start: Optional[int] = None, end: Optional[int] = None) -> bool:
          """
          S.endswith(suffix[, start[, end]]) -> bool
  
          Return True if S ends with the specified suffix, False otherwise.
          With optional start, test S beginning at that position.
          With optional end, stop comparing S at that position.
          suffix can also be a tuple of strings to try.
          """
          if isinstance(suffix, String):
              suffix = suffix.string
          return self.__string.endswith(suffix, start, end)
  
      def expandtabs(self, tabsize: int = 8):
          """
          Return a copy where all tab characters are expanded using spaces.
  
          If tabsize is not given, a tab size of 8 characters is assumed.
          """
          string = self.__string.expandtabs(tabsize)
          return String(string=string)
  
      def find(self, sub: str, __start: Optional[int] = None, __end: Optional[int] = None) -> int:
          """
          S.find(sub[, start[, end]]) -> int
  
          Return the lowest index in S where substring sub is found,
          such that sub is contained within S[start:end].  Optional
          arguments start and end are interpreted as in slice notation.
  
          Return -1 on failure.
          """
          if isinstance(sub, String):
              sub = sub.string
          return self.__string.find(sub, __start, __end)
  
      def format(self, *args, **kwargs):
          """
          S.format(*args, **kwargs) -> str
  
          Return a formatted version of S, using substitutions from args and kwargs.
          The substitutions are identified by braces ('{' and '}').
          """
          string = self.__string.format(args, kwargs)
          return String(string=string)
  
      def format_map(self, mapping: Mapping[str, Any]):
          """
          S.format_map(mapping) -> str
  
          Return a formatted version of S, using substitutions from mapping.
          The substitutions are identified by braces ('{' and '}').
          """
          from .dictionary import Map
          if isinstance(mapping, Map):
              mapping = mapping.dictionary
          string = self.__string.format_map(map=mapping)
          return String(string=string)
  
      def index(self, sub: str, __start: Optional[int] = None, __end: Optional[int] = None) -> int:
          """
          S.index(sub[, start[, end]]) -> int
  
          Return the lowest index in S where substring sub is found,
          such that sub is contained within S[start:end].  Optional
          arguments start and end are interpreted as in slice notation.
  
          Raises ValueError when the substring is not found.
          """
          if isinstance(sub, String):
              sub = sub.string
          return self.__string.index(sub, __start, __end)
  
      def isalnum(self) -> bool:
          """
          Return True if the string is an alpha-numeric string, False otherwise.
  
          A string is alpha-numeric if all characters in the string are alpha-numeric and
          there is at least one character in the string.
          """
          return self.__string.isalnum()
  
      def isalpha(self) -> bool:
          """
          Return True if the string is an alphabetic string, False otherwise.
  
          A string is alphabetic if all characters in the string are alphabetic and there
          is at least one character in the string.
          """
          return self.__string.isalpha()
  
      def isascii(self) -> bool:
          """
          Return True if all characters in the string are ASCII, False otherwise.
  
          ASCII characters have code points in the range U+0000-U+007F.
          Empty string is ASCII too.
          """
          pass
  
      def isdecimal(self) -> bool:
          """
          Return True if the string is a decimal string, False otherwise.
  
          A string is a decimal string if all characters in the string are decimal and
          there is at least one character in the string.
          """
          return self.__string.isdecimal()
  
      def isdigit(self) -> bool:
          """
          Return True if the string is a digit string, False otherwise.
  
          A string is a digit string if all characters in the string are digits and there
          is at least one character in the string.
          """
          return self.__string.isdigit()
  
      def isidentifier(self) -> bool:
          """
          Return True if the string is a valid Python identifier, False otherwise.
  
          Use keyword.iskeyword() to test for reserved identifiers such as "def" and
          "class".
          """
          return self.__string.isidentifier()
  
      def islower(self) -> bool:
          """
          Return True if the string is a lowercase string, False otherwise.
  
          A string is lowercase if all cased characters in the string are lowercase and
          there is at least one cased character in the string.
          """
          return self.__string.islower()
  
      def isnumeric(self) -> bool:
          """
          Return True if the string is a numeric string, False otherwise.
  
          A string is numeric if all characters in the string are numeric and there is at
          least one character in the string.
          """
          return self.__string.isnumeric()
  
      def isprintable(self) -> bool:
          """
          Return True if the string is printable, False otherwise.
  
          A string is printable if all of its characters are considered printable in
          repr() or if it is empty.
          """
          return self.__string.isprintable()
  
      def isspace(self) -> bool:
          """
          Return True if the string is a whitespace string, False otherwise.
  
          A string is whitespace if all characters in the string are whitespace and there
          is at least one character in the string.
          """
          return self.__string.isspace()
  
      def istitle(self) -> bool:
          """
          Return True if the string is a title-cased string, False otherwise.
  
          In a title-cased string, upper- and title-case characters may only
          follow uncased characters and lowercase characters only cased ones.
          """
          return self.__string.istitle()
  
      def isupper(self) -> bool:
          """
          Return True if the string is an uppercase string, False otherwise.
  
          A string is uppercase if all cased characters in the string are uppercase and
          there is at least one cased character in the string.
          """
          return self.__string.isupper()
  
      def join(self, iterable: Iterable[str]):
          """
          Concatenate any number of strings.
  
          The string whose method is called is inserted in between each given string.
          The result is returned as a new string.
  
          Example: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'
          """
          string = self.__string.join(iterable)
          return String(string=string)
  
      def ljust(self, width: int, fillchar: str = ' '):
          """
          Return a left-justified string of length width.
  
          Padding is done using the specified fill character (default is a space).
          """
          if isinstance(fillchar, String):
              fillchar = fillchar.string
          string = self.__string.ljust(width, fillchar)
          return String(string=string)
  
      def lower(self):
          """ Return a copy of the string converted to lowercase. """
          string = self.__string.lower()
          return String(string=string)
  
      def lstrip(self, chars: Optional[str] = None):
          """
          Return a copy of the string with leading whitespace removed.
  
          If chars is given and not None, remove characters in chars instead.
          """
          if isinstance(chars, String):
              chars = chars.string
          string = self.__string.lstrip(chars)
          return String(string=string)
  
      def maketrans(self, *args, **kwargs):
          """
          Return a translation table usable for str.translate().
  
          If there is only one argument, it must be a dictionary mapping Unicode
          ordinals (integers) or characters to Unicode ordinals, strings or None.
          Character keys will be then converted to ordinals.
          If there are two arguments, they must be strings of equal length, and
          in the resulting dictionary, each character in x will be mapped to the
          character at the same position in y. If there is a third argument, it
          must be a string, whose characters will be mapped to None in the result.
          """
          pass
  
      def partition(self, sep: str) -> Tuple[str, str, str]:
          """
          Partition the string into three parts using the given separator.
  
          This will search for the separator in the string.  If the separator is found,
          returns a 3-tuple containing the part before the separator, the separator
          itself, and the part after it.
  
          If the separator is not found, returns a 3-tuple containing the original string
          and two empty strings.
          """
          if isinstance(sep, String):
              sep = sep.string
          return self.__string.partition(sep)
  
      def replace(self, old: str, new: str, count: int = -1):
          """
          Return a copy with all occurrences of substring old replaced by new.
  
            count
              Maximum number of occurrences to replace.
              -1 (the default value) means replace all occurrences.
  
          If the optional argument count is given, only the first count occurrences are
          replaced.
          """
          if isinstance(old, String):
              old = old.string
          if isinstance(new, String):
              new = new.string
          string = self.__string.replace(old, new, count)
          return String(string=string)
  
      def rfind(self, sub: str, __start: Optional[int] = None, __end: Optional[int] = None) -> int:
          """
          S.rfind(sub[, start[, end]]) -> int
  
          Return the highest index in S where substring sub is found,
          such that sub is contained within S[start:end].  Optional
          arguments start and end are interpreted as in slice notation.
  
          Return -1 on failure.
          """
          if isinstance(sub, String):
              sub = sub.string
          return self.__string.rfind(sub, __start, __end)
  
      def rindex(self, sub: str, __start: Optional[int] = None, __end: Optional[int] = None) -> int:
          """
          S.rindex(sub[, start[, end]]) -> int
  
          Return the highest index in S where substring sub is found,
          such that sub is contained within S[start:end].  Optional
          arguments start and end are interpreted as in slice notation.
  
          Raises ValueError when the substring is not found.
          """
          if isinstance(sub, String):
              sub = sub.string
          return self.__string.rindex(sub, __start, __end)
  
      def rjust(self, width: int, fillchar: str = ' '):
          """
          Return a right-justified string of length width.
  
          Padding is done using the specified fill character (default is a space).
          """
          if isinstance(fillchar, String):
              fillchar = fillchar.string
          string = self.__string.rjust(width, fillchar)
          return String(string=string)
  
      def rpartition(self, sep: str) -> Tuple[str, str, str]:
          """
          Partition the string into three parts using the given separator.
  
          This will search for the separator in the string, starting at the end. If
          the separator is found, returns a 3-tuple containing the part before the
          separator, the separator itself, and the part after it.
  
          If the separator is not found, returns a 3-tuple containing two empty strings
          and the original string.
          """
          if isinstance(sep, String):
              sep = sep.string
          return self.__string.rpartition(sep)
  
      def rsplit(self, sep: Optional[str] = None, maxsplit: int = -1) -> List[str]:
          """
          Return a list of the words in the string, using sep as the delimiter string.
  
            sep
              The delimiter according which to split the string.
              None (the default value) means split according to any whitespace,
              and discard empty strings from the result.
            maxsplit
              Maximum number of splits to do.
              -1 (the default value) means no limit.
  
          Splits are done starting at the end of the string and working to the front.
          """
          if isinstance(sep, String):
              sep = sep.string
          return self.__string.rsplit(sep, maxsplit)
  
      def rstrip(self, chars: Optional[str] = None):
          """
          Return a copy of the string with trailing whitespace removed.
  
          If chars is given and not None, remove characters in chars instead.
          """
          if isinstance(chars, String):
              chars = chars.string
          string = self.__string.rstrip(chars)
          return String(string=string)
  
      def split(self, sep: Optional[str] = None, maxsplit: int = -1) -> List[str]:
          """
          Return a list of the words in the string, using sep as the delimiter string.
  
            sep
              The delimiter according which to split the string.
              None (the default value) means split according to any whitespace,
              and discard empty strings from the result.
            maxsplit
              Maximum number of splits to do.
              -1 (the default value) means no limit.
          """
          if isinstance(sep, String):
              sep = sep.string
          return self.__string.split(sep, maxsplit)
  
      def splitlines(self, keepends: bool = False) -> List[str]:
          """
          Return a list of the lines in the string, breaking at line boundaries.
  
          Line breaks are not included in the resulting list unless keepends is given and
          true.
          """
          return self.__string.splitlines(keepends)
  
      def startswith(self, prefix, start: Optional[int] = None, end: Optional[int] = None) -> bool:
          """
          S.startswith(prefix[, start[, end]]) -> bool
  
          Return True if S starts with the specified prefix, False otherwise.
          With optional start, test S beginning at that position.
          With optional end, stop comparing S at that position.
          prefix can also be a tuple of strings to try.
          """
          if isinstance(prefix, String):
              prefix = prefix.string
          return self.__string.startswith(prefix, start, end)
  
      def strip(self, chars: Optional[str] = None):
          """
          Return a copy of the string with leading and trailing whitespace remove.
  
          If chars is given and not None, remove characters in chars instead.
          """
          if isinstance(chars, String):
              chars = chars.string
          string = self.__string.strip(chars)
          return String(string=string)
  
      def swapcase(self):
          """ Convert uppercase characters to lowercase and lowercase characters to uppercase. """
          string = self.__string.swapcase()
          return String(string=string)
  
      def title(self):
          """
          Return a version of the string where each word is titlecased.
  
          More specifically, words start with uppercased characters and all remaining
          cased characters have lower case.
          """
          string = self.__string.title()
          return String(string=string)
  
      def translate(self):
          """
          Replace each character in the string using the given translation table.
  
            table
              Translation table, which must be a mapping of Unicode ordinals to
              Unicode ordinals, strings, or None.
  
          The table must implement lookup/indexing via __getitem__, for instance a
          dictionary or list.  If this operation raises LookupError, the character is
          left untouched.  Characters mapped to None are deleted.
          """
          pass
  
      def upper(self):
          """ Return a copy of the string converted to uppercase. """
          string = self.__string.upper()
          return String(string=string)
  
      def zfill(self, width: int):
          """
          Pad a numeric string with zeros on the left, to fill a field of the given width.
  
          The string is never truncated.
          """
          string = self.__string.zfill(width)
          return String(string=string)
  
      def __add__(self, s: str):
          """ Return self+value. """
          if isinstance(s, String):
              s = s.string
          string = self.__string.__add__(s)
          return String(string=string)
  
      def __contains__(self, s: str) -> bool:
          """ Return key in self. """
          if isinstance(s, String):
              s = s.string
          return self.__string.__contains__(s)
  
      def __eq__(self, x: str) -> bool:
          """ Return self==value. """
          if self is x:
              return True
          if isinstance(x, String):
              x = x.string
          return self.__string.__eq__(x)
  
      def __format__(self, format_spec: str):
          """ Return a formatted version of the string as described by format_spec. """
          if isinstance(format_spec, String):
              format_spec = format_spec.string
          string = self.__string.__format__(format_spec)
          return String(string=string)
  
      # def __getattribute__(self, name: str) -> Any:
      #     """ Return getattr(self, name). """
      #     if isinstance(name, String):
      #         name = name.string
      #     return self.__string.__getattribute__(name=name)
  
      def __getitem__(self, i: Union[int, slice]) -> str:
          """ Return self[key]. """
          return self.__string.__getitem__(i)
  
      def __getnewargs__(self):
          pass
  
      def __ge__(self, x: str) -> bool:
          """ Return self>=value. """
          if isinstance(x, String):
              if self is x:
                  return True
              x = x.string
          return self.__string.__ge__(x)
  
      def __gt__(self, x: str) -> bool:
          """ Return self>value. """
          if isinstance(x, String):
              if self is x:
                  return False
              x = x.string
          return self.__string.__gt__(x)
  
      def __hash__(self) -> int:
          """ Return hash(self). """
          return self.__string.__hash__()
  
      def __iter__(self) -> Iterator[str]:
          """ Implement iter(self). """
          return self.__string.__iter__()
  
      def __len__(self) -> int:
          """ Return len(self). """
          return self.__string.__len__()
  
      def __le__(self, x: str) -> bool:
          """ Return self<=value. """
          if isinstance(x, String):
              if self is x:
                  return True
              x = x.string
          return self.__string.__le__(x)
  
      def __lt__(self, x: str) -> bool:
          """ Return self<value. """
          if isinstance(x, String):
              if self is x:
                  return False
              x = x.string
          return self.__string.__lt__(x)
  
      def __mod__(self, value: Any):
          """ Return self%value. """
          string = self.__string.__mod__(value)
          return String(string=string)
  
      def __mul__(self, n: int):
          """ Return self*value. """
          string = self.__string.__mul__(n)
          return String(string=string)
  
      def __ne__(self, x: str) -> bool:
          """ Return self!=value. """
          if isinstance(x, String):
              if self is x:
                  return False
              x = x.string
          return self.__string.__ne__(x)
  
      def __repr__(self) -> str:
          """ Return repr(self). """
          return self.__string
  
      def __rmod__(self, value: Any):
          """ Return value%self. """
          pass
  
      def __rmul__(self, n: int):
          """ Return value*self. """
          string = self.__string.__rmul__(n)
          return String(string=string)
  
      def __sizeof__(self) -> int:
          """ Return the size of the string in memory, in bytes. """
          return self.__string.__sizeof__()
  
      def __str__(self) -> str:
          """ Return str(self). """
          return self.__string
  