# *****************************************************************************
# Copyright (c) 2020, Intel Corporation All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
#     Redistributions of source code must retain the above copyright notice,
#     this list of conditions and the following disclaimer.
#
#     Redistributions in binary form must reproduce the above copyright notice,
#     this list of conditions and the following disclaimer in the documentation
#     and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
# EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
# *****************************************************************************

"""

| :class:`pandas.core.strings.StringMethods` functions and operators implementations in HPAT

    .. only:: developer

    This is autogenerated sources for all Unicode string functions supported by Numba.
    Currently tested 45 functions only. List of functions obtained automatically from
    `numba.types.misc.UnicodeType` class

    Example of the generated method (for method upper()):
    `hpat_pandas_stringmethods_upper_parallel_impl` is paralell version
    (required additional import mentioned in the body)

    @sdc_overload_method(StringMethodsType, 'upper')
    def hpat_pandas_stringmethods_upper(self):

        ty_checker = TypeChecker('Method stringmethods.upper().')
        ty_checker.check(self, StringMethodsType)

        def hpat_pandas_stringmethods_upper_parallel_impl(self):
            from numba.parfors.parfor import (init_prange, min_checker, internal_prange)

            init_prange()
            result = []
            item_count = len(self._data)
            min_checker(item_count)
            for i in internal_prange(item_count):
                item = self._data[i]
                item_method = item.upper()
                result.append(item_method)

            return pandas.Series(result)

        return hpat_pandas_stringmethods_upper_parallel_impl

        def hpat_pandas_stringmethods_upper_impl(self):
            result = []
            item_count = len(self._data)
            for i in range(item_count):
                item = self._data[i]
                item_method = item.upper()
                result.append(item_method)

            return pandas.Series(result)

        return hpat_pandas_stringmethods_upper_impl

    Test: python -m sdc.runtests sdc.tests.test_hiframes.TestHiFrames.test_str_split_filter

"""


import numpy
import pandas

import numba
from numba.core.types import (Boolean, Integer, NoneType,
                         Omitted, StringLiteral, UnicodeType)

from sdc.utilities.sdc_typing_utils import TypeChecker
from sdc.datatypes.hpat_pandas_stringmethods_types import StringMethodsType
from sdc.utilities.utils import sdc_overload_method, sdc_register_jitable
from sdc.hiframes.api import get_nan_mask
from sdc.str_arr_ext import str_arr_set_na_by_mask, create_str_arr_from_list
from sdc.datatypes.common_functions import SDCLimitation


@sdc_overload_method(StringMethodsType, 'center')
def hpat_pandas_stringmethods_center(self, width, fillchar=' '):
    """
    Intel Scalable Dataframe Compiler User Guide
    ********************************************
    Pandas API: pandas.Series.str.center

    Examples
    --------
    .. literalinclude:: ../../../examples/series/str/series_str_center.py
       :language: python
       :lines: 27-
       :caption: Filling left and right side of strings in the Series with an additional character
       :name: ex_series_str_center

    .. command-output:: python ./series/str/series_str_center.py
       :cwd: ../../../examples

    .. seealso::
        :ref:`Series.str.rjust <pandas.Series.str.rjust>`
            Fills the left side of strings with an arbitrary character.
        :ref:`Series.str.ljust <pandas.Series.str.ljust>`
            Fills the right side of strings with an arbitrary character.

    .. todo:: Add support of 32-bit Unicode for `str.center()`

    Intel Scalable Dataframe Compiler Developer Guide
    *************************************************

    Pandas Series method :meth:`pandas.core.strings.StringMethods.center()` implementation.

    .. only:: developer

    Test: python -m sdc.runtests -k sdc.tests.test_series.TestSeries.test_series_center
    """

    ty_checker = TypeChecker('Method center().')
    ty_checker.check(self, StringMethodsType)

    if not isinstance(width, Integer):
        ty_checker.raise_exc(width, 'int', 'width')

    accepted_types = (Omitted, StringLiteral, UnicodeType)
    if not isinstance(fillchar, accepted_types) and fillchar != ' ':
        ty_checker.raise_exc(fillchar, 'str', 'fillchar')

    def hpat_pandas_stringmethods_center_impl(self, width, fillchar=' '):
        mask = get_nan_mask(self._data._data)
        item_count = len(self._data)
        res_list = [''] * item_count
        for idx in numba.prange(item_count):
            res_list[idx] = self._data._data[idx].center(width, fillchar)
        str_arr = create_str_arr_from_list(res_list)
        result = str_arr_set_na_by_mask(str_arr, mask)

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_center_impl


@sdc_overload_method(StringMethodsType, 'contains')
def hpat_pandas_stringmethods_contains(self, pat, case=True, flags=0, na=None, regex=True):
    """
        Intel Scalable Dataframe Compiler User Guide
        ********************************************
        Pandas API: pandas.Series.str.contains

        Limitations
        -----------
        - Series elements are expected to be Unicode strings. Elements cannot be `NaNs`.
        - Parameter ``na`` is supported only with default value ``None``.
        - Parameter ``flags`` is supported only with default value ``0``.
        - Parameter ``regex`` is supported only with default value ``True``.

        Examples
        --------
        .. literalinclude:: ../../../examples/series/str/series_str_contains.py
           :language: python
           :lines: 27-
           :caption: Tests if string element contains a pattern.
           :name: ex_series_str_contains

        .. command-output:: python ./series/str/series_str_contains.py
           :cwd: ../../../examples

        .. seealso::
            :ref:`Series.str.startswith <pandas.Series.str.startswith>`
                Same as endswith, but tests the start of string.
            :ref:`Series.str.endswith <pandas.Series.str.endswith>`
                Same as startswith, but tests the end of string.

        Intel Scalable Dataframe Compiler Developer Guide
        *************************************************

        Pandas Series method :meth:`pandas.core.strings.StringMethods.contains()` implementation.

        .. only:: developer

        Test: python -m sdc.runtests -k sdc.tests.test_series.TestSeries.test_series_contains
        """

    ty_checker = TypeChecker('Method contains().')
    ty_checker.check(self, StringMethodsType)

    if not isinstance(pat, (StringLiteral, UnicodeType)):
        ty_checker.raise_exc(pat, 'str', 'pat')

    if not isinstance(na, (Omitted, NoneType)) and na is not None:
        ty_checker.raise_exc(na, 'none', 'na')

    if not isinstance(case, (Boolean, Omitted)) and case is not True:
        ty_checker.raise_exc(case, 'bool', 'case')

    if not isinstance(flags, (Omitted, Integer)) and flags != 0:
        ty_checker.raise_exc(flags, 'int64', 'flags')

    if not isinstance(regex, (Omitted, Boolean)) and regex is not True:
        ty_checker.raise_exc(regex, 'bool', 'regex')

    def hpat_pandas_stringmethods_contains_impl(self, pat, case=True, flags=0, na=None, regex=True):
        if flags != 0:
            raise SDCLimitation("Method contains(). Unsupported parameter. Given 'flags' != 0")

        if not regex:
            raise SDCLimitation("Method contains(). Unsupported parameter. Given 'regex' is False")

        if not case:
            _pat = pat.lower()
        else:
            _pat = pat

        len_data = len(self._data)
        res_list = numpy.empty(len_data, numba.types.boolean)
        for idx in numba.prange(len_data):
            res_list[idx] = _pat in self._data._data[idx]

        return pandas.Series(res_list, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_contains_impl


@sdc_overload_method(StringMethodsType, 'endswith')
def hpat_pandas_stringmethods_endswith(self, pat, na=None):
    """
    Intel Scalable Dataframe Compiler User Guide
    ********************************************
    Pandas API: pandas.Series.str.endswith

    Limitations
    -----------
    Series elements are expected to be Unicode strings. Elements cannot be `NaNs`.
    Parameter ``na`` is supported only with default value ``None``.

    Examples
    --------
    .. literalinclude:: ../../../examples/series/str/series_str_endswith.py
       :language: python
       :lines: 27-
       :caption: Test if the end of each string element matches a string
       :name: ex_series_str_endswith

    .. command-output:: python ./series/str/series_str_endswith.py
       :cwd: ../../../examples

    .. seealso::
        `str.endswith <https://docs.python.org/3/library/stdtypes.html#str.endswith>`_
            Python standard library string method.
        :ref:`Series.str.startswith <pandas.Series.str.startswith>`
            Same as endswith, but tests the start of string.
        :ref:`Series.str.contains <pandas.Series.str.contains>`
            Tests if string element contains a pattern.

    .. todo::
        - Add support of matching the end of each string by a pattern
        - Add support of parameter ``na``

    Intel Scalable Dataframe Compiler Developer Guide
    *************************************************

    Pandas Series method :meth:`pandas.core.strings.StringMethods.endswith()` implementation.

    .. only:: developer

    Test: python -m sdc.runtests -k sdc.tests.test_series.TestSeries.test_series_endswith
    """

    ty_checker = TypeChecker('Method endswith().')
    ty_checker.check(self, StringMethodsType)

    if not isinstance(pat, (StringLiteral, UnicodeType)):
        ty_checker.raise_exc(pat, 'str', 'pat')

    if not isinstance(na, (Boolean, NoneType, Omitted)) and na is not None:
        ty_checker.raise_exc(na, 'bool', 'na')

    def hpat_pandas_stringmethods_endswith_impl(self, pat, na=None):
        if na is not None:
            msg = 'Method endswith(). The object na\n expected: None'
            raise ValueError(msg)

        item_endswith = len(self._data)
        result = numpy.empty(item_endswith, numba.types.boolean)
        for idx, item in enumerate(self._data._data):
            result[idx] = item.endswith(pat)

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_endswith_impl


@sdc_overload_method(StringMethodsType, 'find')
def hpat_pandas_stringmethods_find(self, sub, start=0, end=None):
    """
    Intel Scalable Dataframe Compiler User Guide
    ********************************************
    Pandas API: pandas.Series.str.find

    Limitations
    -----------
    Series elements are expected to be Unicode strings. Elements cannot be `NaNs`.
    Parameters ``start``, ``end`` are supported only with default value ``0`` and ``None`` respectively.

    Examples
    --------
    .. literalinclude:: ../../../examples/series/str/series_str_find.py
       :language: python
       :lines: 27-
       :caption: Return lowest indexes in each strings in the Series
       :name: ex_series_str_find

    .. command-output:: python ./series/str/series_str_find.py
       :cwd: ../../../examples

    .. seealso::
        :ref:`Series.str.rfind <pandas.Series.str.rfind>`
            Return highest indexes in each strings.

    .. todo:: Add support of parameters ``start`` and ``end``

    Intel Scalable Dataframe Compiler Developer Guide
    *************************************************

    Pandas Series method :meth:`pandas.core.strings.StringMethods.find()` implementation.

    .. only:: developer

    Test: python -m sdc.runtests -k sdc.tests.test_series.TestSeries.test_series_find
    """

    ty_checker = TypeChecker('Method find().')
    ty_checker.check(self, StringMethodsType)

    if not isinstance(sub, (StringLiteral, UnicodeType)):
        ty_checker.raise_exc(sub, 'str', 'sub')

    accepted_types = (Integer, NoneType, Omitted)
    if not isinstance(start, accepted_types) and start != 0:
        ty_checker.raise_exc(start, 'None, int', 'start')

    if not isinstance(end, accepted_types) and end is not None:
        ty_checker.raise_exc(end, 'None, int', 'end')

    def hpat_pandas_stringmethods_find_impl(self, sub, start=0, end=None):
        if start != 0:
            raise ValueError('Method find(). The object start\n expected: 0')
        if end is not None:
            raise ValueError('Method find(). The object end\n expected: None')

        item_count = len(self._data)
        result = numpy.empty(item_count, numba.types.int64)
        for idx, item in enumerate(self._data._data):
            result[idx] = item.find(sub)

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_find_impl


@sdc_overload_method(StringMethodsType, 'isupper')
def hpat_pandas_stringmethods_isupper(self):
    ty_checker = TypeChecker('Method isupper().')
    ty_checker.check(self, StringMethodsType)

    def hpat_pandas_stringmethods_isupper_impl(self):
        item_count = len(self._data)
        result = numpy.empty(item_count, numba.types.boolean)
        for idx, item in enumerate(self._data._data):
            result[idx] = item.isupper()

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_isupper_impl


@sdc_overload_method(StringMethodsType, 'len')
def hpat_pandas_stringmethods_len(self):
    """
    Intel Scalable Dataframe Compiler User Guide
    ********************************************
    Pandas API: pandas.Series.str.len

    Limitations
    -----------
    Series elements are expected to be Unicode strings. Elements cannot be `NaNs`.

    Examples
    --------
    .. literalinclude:: ../../../examples/series/str/series_str_len.py
       :language: python
       :lines: 27-
       :caption: Compute the length of each element in the Series
       :name: ex_series_str_len

    .. command-output:: python ./series/str/series_str_len.py
       :cwd: ../../../examples

    .. seealso::
        `str.len`
            Python built-in function returning the length of an object.
        :ref:`Series.size <pandas.Series.size>`
            Returns the length of the Series.

    Intel Scalable Dataframe Compiler Developer Guide
    *************************************************

    Pandas Series method :meth:`pandas.core.strings.StringMethods.len()` implementation.

    .. only:: developer

    Test: python -m sdc.runtests sdc.tests.test_series.TestSeries.test_series_str_len1
    """

    ty_checker = TypeChecker('Method len().')
    ty_checker.check(self, StringMethodsType)

    def hpat_pandas_stringmethods_len_impl(self):
        item_count = len(self._data)
        result = numpy.empty(item_count, numba.types.int64)
        for idx, item in enumerate(self._data._data):
            result[idx] = len(item)

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_len_impl


@sdc_overload_method(StringMethodsType, 'ljust')
def hpat_pandas_stringmethods_ljust(self, width, fillchar=' '):
    """
    Intel Scalable Dataframe Compiler User Guide
    ********************************************
    Pandas API: pandas.Series.str.ljust

    Limitations
    -----------
    - This function may reveal slower performance than Pandas* on user system. Users should exercise a tradeoff
    between staying in JIT-region with that function or going back to interpreter mode.

    Examples
    --------
    .. literalinclude:: ../../../examples/series/str/series_str_ljust.py
       :language: python
       :lines: 27-
       :caption: Filling right side of strings in the Series with an additional character
       :name: ex_series_str_ljust

    .. command-output:: python ./series/str/series_str_ljust.py
       :cwd: ../../../examples

    .. seealso::
        :ref:`Series.str.rjust <pandas.Series.str.rjust>`
            Fills the left side of strings with an arbitrary character.
        :ref:`Series.str.center <pandas.Series.str.center>`
            Fills boths sides of strings with an arbitrary character.

    .. todo:: Add support of 32-bit Unicode for `str.ljust()`

    Intel Scalable Dataframe Compiler Developer Guide
    *************************************************

    Pandas Series method :meth:`pandas.core.strings.StringMethods.ljust()` implementation.

    .. only:: developer

    Test: python -m sdc.runtests -k sdc.tests.test_series.TestSeries.test_series_ljust
    """

    ty_checker = TypeChecker('Method ljust().')
    ty_checker.check(self, StringMethodsType)

    if not isinstance(width, Integer):
        ty_checker.raise_exc(width, 'int', 'width')

    accepted_types = (Omitted, StringLiteral, UnicodeType)
    if not isinstance(fillchar, accepted_types) and fillchar != ' ':
        ty_checker.raise_exc(fillchar, 'str', 'fillchar')

    def hpat_pandas_stringmethods_ljust_impl(self, width, fillchar=' '):
        mask = get_nan_mask(self._data._data)
        item_count = len(self._data)
        res_list = [''] * item_count
        for idx in numba.prange(item_count):
            res_list[idx] = self._data._data[idx].ljust(width, fillchar)
        str_arr = create_str_arr_from_list(res_list)
        result = str_arr_set_na_by_mask(str_arr, mask)

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_ljust_impl


@sdc_overload_method(StringMethodsType, 'rjust')
def hpat_pandas_stringmethods_rjust(self, width, fillchar=' '):
    """
    Intel Scalable Dataframe Compiler User Guide
    ********************************************
    Pandas API: pandas.Series.str.rjust

    Examples
    --------
    .. literalinclude:: ../../../examples/series/str/series_str_rjust.py
       :language: python
       :lines: 27-
       :caption: Filling left side of strings in the Series with an additional character
       :name: ex_series_str_rjust

    .. command-output:: python ./series/str/series_str_rjust.py
       :cwd: ../../../examples

    .. seealso::
        :ref:`Series.str.ljust <pandas.Series.str.ljust>`
            Fills the right side of strings with an arbitrary character.
        :ref:`Series.str.center <pandas.Series.str.center>`
            Fills boths sides of strings with an arbitrary character.

    .. todo:: Add support of 32-bit Unicode for `str.rjust()`

    Intel Scalable Dataframe Compiler Developer Guide
    *************************************************

    Pandas Series method :meth:`pandas.core.strings.StringMethods.rjust()` implementation.

    .. only:: developer

    Test: python -m sdc.runtests -k sdc.tests.test_series.TestSeries.test_series_rjust
    """

    ty_checker = TypeChecker('Method rjust().')
    ty_checker.check(self, StringMethodsType)

    if not isinstance(width, Integer):
        ty_checker.raise_exc(width, 'int', 'width')

    accepted_types = (Omitted, StringLiteral, UnicodeType)
    if not isinstance(fillchar, accepted_types) and fillchar != ' ':
        ty_checker.raise_exc(fillchar, 'str', 'fillchar')

    def hpat_pandas_stringmethods_rjust_impl(self, width, fillchar=' '):
        mask = get_nan_mask(self._data._data)
        item_count = len(self._data)
        res_list = [''] * item_count
        for idx in numba.prange(item_count):
            res_list[idx] = self._data._data[idx].rjust(width, fillchar)
        str_arr = create_str_arr_from_list(res_list)
        result = str_arr_set_na_by_mask(str_arr, mask)

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_rjust_impl


@sdc_overload_method(StringMethodsType, 'startswith')
def hpat_pandas_stringmethods_startswith(self, pat, na=None):
    """
    Intel Scalable Dataframe Compiler User Guide
    ********************************************
    Pandas API: pandas.Series.str.startswith

    Limitations
    -----------
    Series elements are expected to be Unicode strings. Elements cannot be `NaNs`.
    Parameter ``na`` is supported only with default value ``None``.

    Examples
    --------
    .. literalinclude:: ../../../examples/series/str/series_str_startswith.py
       :language: python
       :lines: 27-
       :caption: Test if the start of each string element matches a string
       :name: ex_series_str_startswith

    .. command-output:: python ./series/str/series_str_startswith.py
       :cwd: ../../../examples

    .. seealso::
        `str.startswith <https://docs.python.org/3/library/stdtypes.html#str.startswith>`_
            Python standard library string method.
        :ref:`Series.str.endswith <pandas.Series.str.endswith>`
            Same as startswith, but tests the end of string.
        :ref:`Series.str.contains <pandas.Series.str.contains>`
            Tests if string element contains a pattern.

    .. todo::
        - Add support of matching the start of each string by a pattern
        - Add support of parameter ``na``

    Intel Scalable Dataframe Compiler Developer Guide
    *************************************************

    Pandas Series method :meth:`pandas.core.strings.StringMethods.startswith()` implementation.

    .. only:: developer

    Test: python -m sdc.runtests -k sdc.tests.test_series.TestSeries.test_series_startswith
    """

    ty_checker = TypeChecker('Method startswith().')
    ty_checker.check(self, StringMethodsType)

    if not isinstance(pat, (StringLiteral, UnicodeType)):
        ty_checker.raise_exc(pat, 'str', 'pat')

    if not isinstance(na, (Boolean, NoneType, Omitted)) and na is not None:
        ty_checker.raise_exc(na, 'bool', 'na')

    def hpat_pandas_stringmethods_startswith_impl(self, pat, na=None):
        if na is not None:
            msg = 'Method startswith(). The object na\n expected: None'
            raise ValueError(msg)

        item_startswith = len(self._data)
        result = numpy.empty(item_startswith, numba.types.boolean)
        for idx, item in enumerate(self._data._data):
            result[idx] = item.startswith(pat)

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_startswith_impl


@sdc_overload_method(StringMethodsType, 'zfill')
def hpat_pandas_stringmethods_zfill(self, width):
    """
    Intel Scalable Dataframe Compiler User Guide
    ********************************************
    Pandas API: pandas.Series.str.zfill

    Limitations
    -----------
    A leading sign prefix ('+'/'-') is handled by inserting the padding after
    the sign character rather than before.

    Examples
    --------
    .. literalinclude:: ../../../examples/series/str/series_str_zfill.py
       :language: python
       :lines: 27-
       :caption: Pad strings in the Series by prepending '0' characters
       :name: ex_series_str_zfill

    .. command-output:: python ./series/str/series_str_zfill.py
       :cwd: ../../../examples

    .. seealso::
        :ref:`Series.str.rjust <pandas.Series.str.rjust>`
            Fills the left side of strings with an arbitrary character.
        :ref:`Series.str.ljust <pandas.Series.str.ljust>`
            Fills the right side of strings with an arbitrary character.
        :ref:`Series.str.pad <pandas.Series.str.pad>`
            Fills the specified sides of strings with an arbitrary character.
        :ref:`Series.str.center <pandas.Series.str.center>`
            Fills boths sides of strings with an arbitrary character.

    .. todo:: Add support of 32-bit Unicode for `str.zfill()`

    Intel Scalable Dataframe Compiler Developer Guide
    *************************************************

    Pandas Series method :meth:`pandas.core.strings.StringMethods.zfill()` implementation.

    .. only:: developer

    Test: python -m sdc.runtests -k sdc.tests.test_series.TestSeries.test_series_zfill
    """

    ty_checker = TypeChecker('Method zfill().')
    ty_checker.check(self, StringMethodsType)

    if not isinstance(width, Integer):
        ty_checker.raise_exc(width, 'int', 'width')

    def hpat_pandas_stringmethods_zfill_impl(self, width):
        mask = get_nan_mask(self._data._data)
        item_count = len(self._data)
        res_list = [''] * item_count
        for idx in numba.prange(item_count):
            res_list[idx] = self._data._data[idx].zfill(width)
        str_arr = create_str_arr_from_list(res_list)
        result = str_arr_set_na_by_mask(str_arr, mask)

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_zfill_impl


sdc_pandas_series_str_docstring_template = """
        Intel Scalable Dataframe Compiler User Guide
        ********************************************
        Pandas API: pandas.Series.str.{method_name}
        {limitations}
        Examples
        --------
        .. literalinclude:: ../../../examples/series/str/series_str_{method_name}.py
           :language: python
           :lines: 27-
           :caption: {caption}
           :name: ex_series_str_{method_name}

        .. command-output:: python ./series/str/series_str_{method_name}.py
           :cwd: ../../../examples
        {seealso}
        Intel Scalable Dataframe Compiler Developer Guide
        *************************************************

        Pandas Series method :meth:`pandas.core.strings.StringMethods.{method_name}()` implementation.

        .. only:: developer

        Test: python -m sdc.runtests sdc.tests.test_series.TestSeries.test_series_{method_name}_str
"""


@sdc_overload_method(StringMethodsType, 'istitle')
def hpat_pandas_stringmethods_istitle(self):

    ty_checker = TypeChecker('Method istitle().')
    ty_checker.check(self, StringMethodsType)

    def hpat_pandas_stringmethods_istitle_impl(self):
        item_count = len(self._data)
        result = numpy.empty(item_count, numba.types.boolean)
        for idx, item in enumerate(self._data._data):
            result[idx] = item.istitle()

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_istitle_impl


@sdc_overload_method(StringMethodsType, 'isspace')
def hpat_pandas_stringmethods_isspace(self):

    ty_checker = TypeChecker('Method isspace().')
    ty_checker.check(self, StringMethodsType)

    def hpat_pandas_stringmethods_isspace_impl(self):
        item_count = len(self._data)
        result = numpy.empty(item_count, numba.types.boolean)
        for idx, item in enumerate(self._data._data):
            result[idx] = item.isspace()

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_isspace_impl


@sdc_overload_method(StringMethodsType, 'isalpha')
def hpat_pandas_stringmethods_isalpha(self):

    ty_checker = TypeChecker('Method isalpha().')
    ty_checker.check(self, StringMethodsType)

    def hpat_pandas_stringmethods_isalpha_impl(self):
        item_count = len(self._data)
        result = numpy.empty(item_count, numba.types.boolean)
        for idx, item in enumerate(self._data._data):
            result[idx] = item.isalpha()

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_isalpha_impl


@sdc_overload_method(StringMethodsType, 'islower')
def hpat_pandas_stringmethods_islower(self):

    ty_checker = TypeChecker('Method islower().')
    ty_checker.check(self, StringMethodsType)

    def hpat_pandas_stringmethods_islower_impl(self):
        item_count = len(self._data)
        result = numpy.empty(item_count, numba.types.boolean)
        for idx, item in enumerate(self._data._data):
            result[idx] = item.islower()

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_islower_impl


@sdc_overload_method(StringMethodsType, 'isalnum')
def hpat_pandas_stringmethods_isalnum(self):

    ty_checker = TypeChecker('Method isalnum().')
    ty_checker.check(self, StringMethodsType)

    def hpat_pandas_stringmethods_isalnum_impl(self):
        item_count = len(self._data)
        result = numpy.empty(item_count, numba.types.boolean)
        for idx, item in enumerate(self._data._data):
            result[idx] = item.isalnum()

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_isalnum_impl


@sdc_overload_method(StringMethodsType, 'isnumeric')
def hpat_pandas_stringmethods_isnumeric(self):
    ty_checker = TypeChecker('Method isnumeric().')
    ty_checker.check(self, StringMethodsType)

    def hpat_pandas_stringmethods_isnumeric_impl(self):
        item_count = len(self._data)
        result = numpy.empty(item_count, numba.types.boolean)
        for idx, item in enumerate(self._data._data):
            result[idx] = item.isnumeric()

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_isnumeric_impl


@sdc_overload_method(StringMethodsType, 'isdigit')
def hpat_pandas_stringmethods_isdigit(self):
    ty_checker = TypeChecker('Method isdigit().')
    ty_checker.check(self, StringMethodsType)

    def hpat_pandas_stringmethods_isdigit_impl(self):
        item_count = len(self._data)
        result = numpy.empty(item_count, numba.types.boolean)
        for idx, item in enumerate(self._data._data):
            result[idx] = item.isdigit()

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_isdigit_impl


@sdc_overload_method(StringMethodsType, 'isdecimal')
def hpat_pandas_stringmethods_isdecimal(self):
    ty_checker = TypeChecker('Method isdecimal().')
    ty_checker.check(self, StringMethodsType)

    def hpat_pandas_stringmethods_isdecimal_impl(self):
        item_count = len(self._data)
        result = numpy.empty(item_count, numba.types.boolean)
        for idx, item in enumerate(self._data._data):
            result[idx] = item.isdecimal()

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_isdecimal_impl


@sdc_overload_method(StringMethodsType, 'capitalize')
def hpat_pandas_stringmethods_capitalize(self):
    ty_checker = TypeChecker('Method capitalize().')
    ty_checker.check(self, StringMethodsType)

    def hpat_pandas_stringmethods_capitalize_impl(self):
        mask = get_nan_mask(self._data._data)
        item_count = len(self._data)
        res_list = [''] * item_count
        for idx in numba.prange(item_count):
            res_list[idx] = self._data._data[idx].capitalize()
        str_arr = create_str_arr_from_list(res_list)
        result = str_arr_set_na_by_mask(str_arr, mask)

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_capitalize_impl


@sdc_overload_method(StringMethodsType, 'title')
def hpat_pandas_stringmethods_title(self):
    ty_checker = TypeChecker('Method title().')
    ty_checker.check(self, StringMethodsType)

    def hpat_pandas_stringmethods_title_impl(self):
        mask = get_nan_mask(self._data._data)
        item_count = len(self._data)
        res_list = [''] * item_count
        for idx in numba.prange(item_count):
            res_list[idx] = self._data._data[idx].title()
        str_arr = create_str_arr_from_list(res_list)
        result = str_arr_set_na_by_mask(str_arr, mask)

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_title_impl


@sdc_overload_method(StringMethodsType, 'swapcase')
def hpat_pandas_stringmethods_swapcase(self):
    ty_checker = TypeChecker('Method swapcase().')
    ty_checker.check(self, StringMethodsType)

    def hpat_pandas_stringmethods_swapcase_impl(self):
        mask = get_nan_mask(self._data._data)
        item_count = len(self._data)
        res_list = [''] * item_count
        for idx in numba.prange(item_count):
            res_list[idx] = self._data._data[idx].swapcase()
        str_arr = create_str_arr_from_list(res_list)
        result = str_arr_set_na_by_mask(str_arr, mask)

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_swapcase_impl


@sdc_overload_method(StringMethodsType, 'casefold')
def hpat_pandas_stringmethods_casefold(self):
    ty_checker = TypeChecker('Method casefold().')
    ty_checker.check(self, StringMethodsType)

    def hpat_pandas_stringmethods_casefold_impl(self):
        mask = get_nan_mask(self._data._data)
        item_count = len(self._data)
        res_list = [''] * item_count
        for idx in numba.prange(item_count):
            res_list[idx] = self._data._data[idx].casefold()
        str_arr = create_str_arr_from_list(res_list)
        result = str_arr_set_na_by_mask(str_arr, mask)

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_casefold_impl


@sdc_overload_method(StringMethodsType, 'lower')
def hpat_pandas_stringmethods_lower(self):
    ty_checker = TypeChecker('Method lower().')
    ty_checker.check(self, StringMethodsType)

    def hpat_pandas_stringmethods_lower_impl(self):
        mask = get_nan_mask(self._data._data)
        item_count = len(self._data)
        res_list = [''] * item_count

        for it in range(item_count):
            item = self._data._data[it]
            if len(item) > 0:
                res_list[it] = item.lower()
            else:
                res_list[it] = item

        str_arr = create_str_arr_from_list(res_list)
        result = str_arr_set_na_by_mask(str_arr, mask)

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_lower_impl


@sdc_overload_method(StringMethodsType, 'upper')
def hpat_pandas_stringmethods_upper(self):
    ty_checker = TypeChecker('Method upper().')
    ty_checker.check(self, StringMethodsType)

    def hpat_pandas_stringmethods_upper_impl(self):
        mask = get_nan_mask(self._data._data)
        item_count = len(self._data)
        result = [''] * item_count

        for it in numba.prange(item_count):
            item = self._data._data[it]
            if len(item) > 0:
                result[it] = item.upper()
            else:
                result[it] = item

        str_arr = create_str_arr_from_list(result)
        result = str_arr_set_na_by_mask(str_arr, mask)

        return pandas.Series(result, self._data._index, name=self._data._name)

    return hpat_pandas_stringmethods_upper_impl


@sdc_register_jitable
def lstrip_usecase(s, to_strip):
    return s.lstrip(to_strip)


@sdc_register_jitable
def rstrip_usecase(s, to_strip):
    return s.rstrip(to_strip)


@sdc_register_jitable
def strip_usecase(s, to_strip):
    return s.strip(to_strip)


def gen_sdc_pandas_series_str_strip_impl(usecase):
    """Generate series.str.lstrip/rstrip/strip implementations based on usecase func"""
    def impl(self, to_strip=None):
        mask = get_nan_mask(self._data._data)
        item_count = len(self._data)
        res_list = [''] * item_count

        for it in range(item_count):
            item = self._data._data[it]
            if len(item) > 0:
                res_list[it] = usecase(item, to_strip)
            else:
                res_list[it] = item

        str_arr = create_str_arr_from_list(res_list)
        result = str_arr_set_na_by_mask(str_arr, mask)

        return pandas.Series(result, self._data._index, name=self._data._name)

    return impl


sdc_pandas_series_str_lstrip_impl = gen_sdc_pandas_series_str_strip_impl(lstrip_usecase)
sdc_pandas_series_str_rstrip_impl = gen_sdc_pandas_series_str_strip_impl(rstrip_usecase)
sdc_pandas_series_str_strip_impl = gen_sdc_pandas_series_str_strip_impl(strip_usecase)


@sdc_overload_method(StringMethodsType, 'lstrip')
def hpat_pandas_stringmethods_lstrip(self, to_strip=None):
    ty_checker = TypeChecker('Method strip().')
    ty_checker.check(self, StringMethodsType)

    if not isinstance(to_strip, (NoneType, StringLiteral, UnicodeType, Omitted)) and to_strip is not None:
        ty_checker.raise_exc(to_strip, 'str', 'to_strip')

    return sdc_pandas_series_str_lstrip_impl


@sdc_overload_method(StringMethodsType, 'rstrip')
def hpat_pandas_stringmethods_rstrip(self, to_strip=None):
    ty_checker = TypeChecker('Method rstrip().')
    ty_checker.check(self, StringMethodsType)

    if not isinstance(to_strip, (NoneType, StringLiteral, UnicodeType, Omitted)) and to_strip is not None:
        ty_checker.raise_exc(to_strip, 'str', 'to_strip')

    return sdc_pandas_series_str_rstrip_impl


@sdc_overload_method(StringMethodsType, 'strip')
def hpat_pandas_stringmethods_strip(self, to_strip=None):
    ty_checker = TypeChecker('Method strip().')
    ty_checker.check(self, StringMethodsType)

    if not isinstance(to_strip, (NoneType, StringLiteral, UnicodeType, Omitted)) and to_strip is not None:
        ty_checker.raise_exc(to_strip, 'str', 'to_strip')

    return sdc_pandas_series_str_strip_impl


seealso_check_methods = """
        .. seealso::
            :ref:`Series.str.isalpha <pandas.Series.str.isalpha>`
                Checks whether all characters are alphabetic.
            :ref:`Series.str.isnumeric <pandas.Series.str.isnumeric>`
                Checks whether all characters are numeric.
            :ref:`Series.str.isalnum <pandas.Series.str.isalnum>`
                Checks whether all characters are alphanumeric.
            :ref:`Series.str.isdigit <pandas.Series.str.isdigit>`
                Checks whether all characters are digits.
            :ref:`Series.str.isdecimal <pandas.Series.str.isdecimal>`
                Checks whether all characters are decimal.
            :ref:`Series.str.isspace <pandas.Series.str.isspace>`
                Checks whether all characters are whitespace.
            :ref:`Series.str.islower <pandas.Series.str.islower>`
                Checks whether all characters are lowercase.
            :ref:`Series.str.isupper <pandas.Series.str.isupper>`
                Checks whether all characters are uppercase.
            :ref:`Series.str.istitle <pandas.Series.str.istitle>`
                Checks whether all characters are titlecase.
"""

seealso_transform_methods = """
        .. seealso::
            :ref:`Series.str.lower <pandas.Series.str.lower>`
                Converts all characters to lowercase.
            :ref:`Series.str.upper <pandas.Series.str.upper>`
                Converts all characters to uppercase.
            :ref:`Series.str.title <pandas.Series.str.title>`
                Converts first character of each word to uppercase and remaining to lowercase.
            :ref:`Series.str.capitalize <pandas.Series.str.capitalize>`
                Converts first character to uppercase and remaining to lowercase.
            :ref:`Series.str.swapcase <pandas.Series.str.swapcase>`
                Converts uppercase to lowercase and lowercase to uppercase.
            :ref:`Series.str.casefold <pandas.Series.str.casefold>`
                Removes all case distinctions in the string.
"""

limitation_nans_unsupported = """
        Limitations
        -----------
        Series elements are expected to be Unicode strings. Elements cannot be `NaNs`.
"""

limitation_nans_supported = """
        Limitations
        -----------
        All values in Series equal to `None` are converted to `NaNs`.
"""

seealso_strip_methods = """
        .. seealso::
            :ref:`Series.str.strip <pandas.Series.str.strip>`
                Remove leading and trailing characters in Series.
            :ref:`Series.str.lstrip <pandas.Series.str.lstrip>`
                Remove leading characters in Series.
            :ref:`Series.str.strip <pandas.Series.str.strip>`
                Remove trailing characters in Series.
"""


stringmethods_funcs = {
    'istitle': {
        'method': hpat_pandas_stringmethods_istitle,
        'caption': 'Check if each word start with an upper case letter',
        'seealso': seealso_check_methods,
        'limitations':
        """
        Limitations
        -----------
        This function may reveal slower performance than Pandas* on user system. Users should exercise a tradeoff
        between staying in JIT-region with that function or going back to interpreter mode.
        """
    },
    'isspace': {
        'method': hpat_pandas_stringmethods_isspace,
        'caption': 'Check if all the characters in the text are whitespaces',
        'seealso': seealso_check_methods,
        'limitations':
        """
        Limitations
        -----------
        This function may reveal slower performance than Pandas* on user system. Users should exercise a tradeoff
        between staying in JIT-region with that function or going back to interpreter mode.
        """
    },
    'isalpha': {
        'method': hpat_pandas_stringmethods_isalpha,
        'caption': 'Check whether all characters in each string are alphabetic',
        'seealso': seealso_check_methods,
        'limitations':
        """
        Limitations
        -----------
        This function may reveal slower performance than Pandas* on user system. Users should exercise a tradeoff
        between staying in JIT-region with that function or going back to interpreter mode.
        """
    },
    'islower': {
        'method': hpat_pandas_stringmethods_islower,
        'caption': 'Check if all the characters in the text are alphanumeric',
        'seealso': seealso_check_methods,
        'limitations':
        """
        Limitations
        -----------
        This function may reveal slower performance than Pandas* on user system. Users should exercise a tradeoff
        between staying in JIT-region with that function or going back to interpreter mode.
        """
    },
    'isalnum': {
        'method': hpat_pandas_stringmethods_isalnum,
        'caption': 'Check if all the characters in the text are alphanumeric',
        'seealso': seealso_check_methods,
        'limitations':
        """
        Limitations
        -----------
        This function may reveal slower performance than Pandas* on user system. Users should exercise a tradeoff
        between staying in JIT-region with that function or going back to interpreter mode.
        """
    },
    'isnumeric': {
        'method': hpat_pandas_stringmethods_isnumeric,
        'caption': 'Check whether all characters in each string are numeric.',
        'seealso': seealso_check_methods,
        'limitations': limitation_nans_unsupported
    },
    'isdigit': {
        'method': hpat_pandas_stringmethods_isdigit,
        'caption': 'Check whether all characters in each string in the Series are digits.',
        'seealso': seealso_check_methods,
        'limitations': limitation_nans_unsupported
    },
    'isdecimal': {
        'method': hpat_pandas_stringmethods_isdecimal,
        'caption': 'Check whether all characters in each string are decimal.',
        'seealso': seealso_check_methods,
        'limitations': limitation_nans_unsupported
    },
    'isupper': {
        'method': hpat_pandas_stringmethods_isupper,
        'caption': 'Check whether all characters in each string are uppercase.',
        'seealso': seealso_check_methods,
        'limitations':
        """
        Limitations
        -----------
        This function may reveal slower performance than Pandas* on user system. Users should exercise a tradeoff
        between staying in JIT-region with that function or going back to interpreter mode.
        """
    },
    'capitalize': {
        'method': hpat_pandas_stringmethods_capitalize,
        'caption': 'Convert strings in the Series to be capitalized.',
        'seealso': seealso_transform_methods,
        'limitations':
        """
        Limitations
        -----------
        - All values in Series equal to `None` are converted to `NaNs`.
        - This function may reveal slower performance than Pandas* on user system. Users should exercise a tradeoff
        between staying in JIT-region with that function or going back to interpreter mode.
        """
    },
    'title': {
        'method': hpat_pandas_stringmethods_title,
        'caption': 'Convert strings in the Series to titlecase.',
        'seealso': seealso_transform_methods,
        'limitations':
        """
        Limitations
        -----------
        - All values in Series equal to `None` are converted to `NaNs`.
        - This function may reveal slower performance than Pandas* on user system. Users should exercise a tradeoff
        between staying in JIT-region with that function or going back to interpreter mode.
        """
    },
    'swapcase': {
        'method': hpat_pandas_stringmethods_swapcase,
        'caption': 'Convert strings in the Series to be swapcased.',
        'seealso': seealso_transform_methods,
        'limitations':
        """
        Limitations
        -----------
        - All values in Series equal to `None` are converted to `NaNs`.
        - This function may reveal slower performance than Pandas* on user system. Users should exercise a tradeoff
        between staying in JIT-region with that function or going back to interpreter mode.
        """
    },
    'casefold': {
        'method': hpat_pandas_stringmethods_casefold,
        'caption': 'Convert strings in the Series to be casefolded.',
        'seealso': seealso_transform_methods,
        'limitations':
        """
        Limitations
        -----------
        - All values in Series equal to `None` are converted to `NaNs`.
        - This function may reveal slower performance than Pandas* on user system. Users should exercise a tradeoff
        between staying in JIT-region with that function or going back to interpreter mode.
        """
    },
    'strip': {
        'method': hpat_pandas_stringmethods_strip,
        'caption': 'Remove leading and trailing characters.',
        'seealso': seealso_strip_methods,
        'limitations':
        """
        Limitations
        -----------
        - All values in Series equal to `None` are converted to `NaNs`.
        - This function may reveal slower performance than Pandas* on user system. Users should exercise a tradeoff
        between staying in JIT-region with that function or going back to interpreter mode.
        """
    },
    'lstrip': {
        'method': hpat_pandas_stringmethods_lstrip,
        'caption': 'Remove leading and trailing characters.',
        'seealso': seealso_strip_methods,
        'limitations':
        """
        Limitations
        -----------
        - All values in Series equal to `None` are converted to `NaNs`.
        - This function may reveal slower performance than Pandas* on user system. Users should exercise a tradeoff
        between staying in JIT-region with that function or going back to interpreter mode.
        """
    },
    'rstrip': {
        'method': hpat_pandas_stringmethods_rstrip,
        'caption': 'Remove leading and trailing characters.',
        'seealso': seealso_strip_methods,
        'limitations':
        """
        Limitations
        -----------
        - All values in Series equal to `None` are converted to `NaNs`.
        - This function may reveal slower performance than Pandas* on user system. Users should exercise a tradeoff
        between staying in JIT-region with that function or going back to interpreter mode.
        """
    },
    'lower': {
        'method': hpat_pandas_stringmethods_lower,
        'caption': 'Convert strings in the Series to lowercase.',
        'seealso': seealso_transform_methods,
        'limitations':
        """
        Limitations
        -----------
        - All values in Series equal to `None` are converted to `NaNs`.
        - This function may reveal slower performance than Pandas* on user system. Users should exercise a tradeoff
        between staying in JIT-region with that function or going back to interpreter mode.
        """
    },
    'upper': {
        'method': hpat_pandas_stringmethods_upper,
        'caption': 'Convert strings in the Series to upper case.',
        'seealso': seealso_transform_methods,
        'limitations':
        """
        Limitations
        -----------
        - All values in Series equal to `None` are converted to `NaNs`.
        - This function may reveal slower performance than Pandas* on user system. Users should exercise a tradeoff
        between staying in JIT-region with that function or going back to interpreter mode.
        """
    },
}

for name, data in stringmethods_funcs.items():
    data['method'].__doc__ = sdc_pandas_series_str_docstring_template.format(
        **{'method_name': name,
           'caption': data['caption'],
           'seealso': data['seealso'],
           'limitations': data['limitations']
           }
    )
