def transcript_pos_transform(tobj, transcript_pos):
    """
	transform the transcript position to genomic position
	0-based
	if strand is "-", return position of strand.
	"""
    length = 0
    strand = tobj.genomic_iv.strand
    genomic_pos = -1
    for i in tobj.genomic_exons:
        if transcript_pos < length + i.length:
            if strand == '+':
                genomic_pos = transcript_pos - length + i.start
            else:
                genomic_pos = i.start_d - (transcript_pos - length)
            break
        length += i.length
    return genomic_pos


def remove_attrs(dictionary, *attrs):
    """Returns a copy of dictionary without attrs"""
    result = {}
    for key, value in dictionary.items():
        if key not in attrs:
            result[key] = value
    return result


def knn_kfinder(xtr, xt, ytr, yt, lower=1, upper=30):
    """
    Author: Aru Raghuvanshi

    This function plots the KNN elbow plot to figure out
    the best value for K in the KNN Classifier.

    Arguments: (xtr, xt, ytr, yt, lower=1, upper=30)
    Returns: Plot

    Example: p = knn_plot(X_train, X_test, y_train, y_test, 1, 30)

    """
    from sklearn.neighbors import KNeighborsClassifier
    from sklearn.metrics import accuracy_score
    import matplotlib.pyplot as plt
    plt.style.use('seaborn')
    krange = range(lower, upper)
    acc_score = []
    for i in krange:
        knn = KNeighborsClassifier(n_neighbors=i)
        knn.fit(xtr, ytr)
        pred_i = knn.predict(xt)
        acc_score.append(accuracy_score(yt, pred_i))
    plt.figure(figsize=(14, 6))
    plt.plot(krange, acc_score, color='salmon', linestyle='solid', marker=
        'o', mfc='purple', label='K vs Testing Accuracy', lw=2)
    plt.title('KNN K-GRAPH')
    plt.xlabel('K', fontsize=18)
    plt.ylabel('Test Accuracy', fontsize=16)
    plt.legend()
    plt.show()


@staticmethod
def _get_list_of_neighbour_coord(xpos_new, ypos_new, level, num_tiles_x,
    num_tiles_y):
    """
        Finds the neighbours of a tile and returns them

        :Parameters:
            xpos_new : int
                x position
            ypos_new : int
                y position
            level : int
                collapse level because this uses original tiles
            num_tiles_x : int
                number of tiles in x direction
            num_tiles_y : int
                number of tiles in y direction
        :Returns:
            list of coordinates of the neighbour tiles
        """
    xpos = xpos_new * level
    ypos = ypos_new * level
    coords = []
    for y in range(ypos, ypos + level):
        for x in range(xpos, xpos + level):
            if x <= num_tiles_x and y <= num_tiles_y:
                coords.append((x, y))
    return coords


def packages_to_dict(request, packages):
    """ Convert a list of packages to a dict used by the template """
    pkgs = {}
    for package in packages:
        pkgs[package.filename] = {'url': package.get_url(request),
            'requires_python': package.data.get('requires_python')}
    return pkgs


def set_password(new_password, processor_name, processor):
    """
        Sets user password to `new_password` using given password `processor`
        registered in component registry as `processor_name`.
        """


def items():
    """Returns an iterable of available ICachableItem in the ICachableSource"""


def iterable(obj):
    """ Return True if obj is iterable with a well-defined len()."""
    if hasattr(obj, '__iter__'):
        return True
    try:
        len(obj)
    except:
        return False
    return True


def StripUnicode(string, subs='.'):
    """Strip non-ASCII characters from strings

    :param str string: string to strip Unicode characters from
    :param str subs: character(s) to place into string in place of each
      Unicode character. Defaults to '.'

    :returns: a new string with only ASCII characters
    """
    s = ''
    for c in string:
        if ord(c) < 128:
            s += c
        else:
            s += subs
    return s


def parse_bool(val):
    """Parse a bool value.

    Handles a series of values, but you should probably standardize on
    "true" and "false".

    >>> parse_bool('y')
    True
    >>> parse_bool('FALSE')
    False

    """
    true_vals = 't', 'true', 'yes', 'y', '1', 'on'
    false_vals = 'f', 'false', 'no', 'n', '0', 'off'
    val = val.lower()
    if val in true_vals:
        return True
    if val in false_vals:
        return False
    raise ValueError('"%s" is not a valid bool value' % val)


def minima_phases(max_min_dictionary):
    """
    Find the phases of minima.

    Minima are found by finding the minimum value between sets of two peaks.
    The phase of the minimum relative to the two peaks is then returned.
    i.e the fraction of time elapsed between the two peaks when the minimum
    occurs is returned.

    It is very important to make sure the correct delta is specified for
    peak discrimination, otherwise unexpected results may be returned.

    :param max_min_dictionary: max_min_dictionary

    :return: phase of minimum relative to peaks.

    """
    minima_num = max_min_dictionary['minima_number']
    maxima_num = max_min_dictionary['maxima_number']
    maxima_times = max_min_dictionary['maxima_times']
    minima_times = max_min_dictionary['minima_times']
    minima_phases = []
    for i in range(min(minima_num, maxima_num - 1)):
        maximum_0_t = maxima_times[i]
        maximum_1_t = maxima_times[i + 1]
        minimum_time = minima_times[i]
        phase = (minimum_time - maximum_0_t) / (maximum_1_t - maximum_0_t)
        minima_phases.append(phase)
    phase_list = [minima_times, minima_phases]
    return phase_list


def _test3():
    """
    >>> vtor.check('string(default="")', '', missing=True)
    ''
    >>> vtor.check('string(default="\\n")', '', missing=True)
    '\\n'
    >>> print(vtor.check('string(default="\\n")', '', missing=True))
    <BLANKLINE>
    <BLANKLINE>
    >>> vtor.check('string()', '\\n')
    '\\n'
    >>> vtor.check('string(default="\\n\\n\\n")', '', missing=True)
    '\\n\\n\\n'
    >>> vtor.check('string()', 'random \\n text goes here\\n\\n')
    'random \\n text goes here\\n\\n'
    >>> vtor.check('string(default=" \\nrandom text\\ngoes \\n here\\n\\n ")',
    ... '', missing=True)
    ' \\nrandom text\\ngoes \\n here\\n\\n '
    >>> vtor.check("string(default='\\n\\n\\n')", '', missing=True)
    '\\n\\n\\n'
    >>> vtor.check("option('\\n','a','b',default='\\n')", '', missing=True)
    '\\n'
    >>> vtor.check("string_list()", ['foo', '\\n', 'bar'])
    ['foo', '\\n', 'bar']
    >>> vtor.check("string_list(default=list('\\n'))", '', missing=True)
    ['\\n']
    """


def calculate_z_centr(df):
    """
    Assign the central z to each layer of an existing DataFrame.

    :param df: Original DataFrame.
    :return: Dataframe with the z_centr column.
    """
    return df.assign(z_centr=(df['zf'] + df['z_in']) / 2)


def _ambiguity_keep_most_reads(table, ambig_map):
    """Keep the ambiguous sample with the most reads

    Parameters
    ----------
    table : biom.Table
        The table obtained from redbiom
    ambig_map : dict
        A mapping of a sample ID in the table to its ambiguous form.

    Returns
    -------
    biom.Table
        A table of the most volumous data with updated sample identifiers
    """
    import pandas as pd
    if set(ambig_map) != set(table.ids()):
        raise ValueError('IDs are inconsistent')
    sample_counts = pd.Series(table.sum('sample'), index=table.ids()).to_dict()
    ambigs = {}
    for k, v in ambig_map.items():
        if v not in ambigs:
            ambigs[v] = []
        ambigs[v].append(k)
    to_keep = []
    for sample_name, sample_ids in ambigs.items():
        if len(sample_ids) > 1:
            best = sample_ids[0]
            best_cnt = sample_counts[best]
            for i in sample_ids[1:]:
                cnt = sample_counts[i]
                if cnt > best_cnt:
                    best = i
                    best_cnt = cnt
            to_keep.append(best)
        else:
            to_keep.append(sample_ids[0])
    subset_table = table.filter(set(to_keep), inplace=False).remove_empty()
    subset_table.update_ids(ambig_map, inplace=True)
    return subset_table


def format_header(header, tchar):
    """Given a string and title char return a reStructuredText style section
    header.

    :param header: The header text to format
    :param tchar: The character to use.

    :type header: str
    :type tchar: str

    :rtype: str
    :returns: The formatted title.
    """
    return header + '\n' + tchar * len(header)


def is_pyarrow_installed():
    """
    Check if pyarrow is installed
    :return:
    """
    try:
        import pyarrow
        have_arrow = True
    except ImportError:
        have_arrow = False
    return have_arrow


def artistinfo():
    """
    @api {post} /music/moreinfo/artistinfo 请求歌手信息
    @apiVersion 1.0.0
    @apiGroup More
    @apiDescription 请求歌手信息

    @apiParam (Headers) {string} token 令牌
    @apiParam (Headers) {String} device_token 设备令牌
    @apiParam (Headers) {String} content_type 内容类型
    @apiParam (Headers) {String} timestamp 时间戳

    @apiParam (Body) {String} artistid 歌手编号

    @apiSuccess (Success) {Object} response 响应
    @apiSuccess (Success) {String} response.start 开始
    @apiSuccess (Success) {String} response.docs 响应文档

    @apiSuccess (Success) {String} response.docs.datainfo 数据信息
    @apiSuccess (Success) {String} response.docs.datainfo.genre 流派类型
    @apiSuccess (Success) {String} response.docs.datainfo.bitrates 比特率
    @apiSuccess (Success) {String} response.docs.datainfo.releasedate 发布日期
    @apiSuccess (Success) {String} response.docs.datainfo.label 标签
    @apiSuccess (Success) {String} response.docs.datainfo.exclusivity 歌曲版权付费模式（FREE，Normal，Purchase，ALAKA，Streaming，Cache）
    @apiSuccess (Success) {String} response.docs.datainfo.translatename 翻译名称
    @apiSuccess (Success) {String} response.docs.datainfo.version 版本
    @apiSuccess (Success) {String} response.docs.datainfo.duration 歌曲长度
    @apiSuccess (Success) {String} response.docs.datainfo.side 歌曲位于专辑的片（面）数
    @apiSuccess (Success) {String} response.docs.datainfo.explicit 脏标
    @apiSuccess (Success) {String} response.docs.datainfo.description 描述
    @apiSuccess (Success) {String} response.docs.datainfo.name 名称
    @apiSuccess (Success) {String} response.docs.datainfo.tracknumber 声音编号
    @apiSuccess (Success) {String} response.docs.datainfo.rating 收听总数
    @apiSuccess (Success) {String} response.docs.datainfo.cpItemID 出版公司单曲编号
    @apiSuccess (Success) {String} response.docs.datainfo.pinyinname 歌曲拼音名称
    @apiSuccess (Success) {String} response.docs.datainfo.languagetype 语言类型
    @apiSuccess (Success) {String} response.docs.datainfo.priceCode 价格代号

    @apiSuccess (Success) {String} response.docs.artists 歌手列表
    @apiSuccess (Success) {String} response.docs.artists.genre 歌手流派类型
    @apiSuccess (Success) {String} response.docs.artists.region 歌手区域
    @apiSuccess (Success) {String} response.docs.artists.birthday 歌手生日
    @apiSuccess (Success) {String} response.docs.artists.itemcount 歌手单曲数量
    @apiSuccess (Success) {String} response.docs.artists.translatename 歌手翻译名称
    @apiSuccess (Success) {String} response.docs.artists.imagePathMap 歌手图像路径图
    @apiSuccess (Success) {String} response.docs.artists.imagePathMap.value 歌手图像路径图值
    @apiSuccess (Success) {String} response.docs.artists.imagePathMap.key 歌手图像路径图键
    @apiSuccess (Success) {String} response.docs.artists.artistID 歌手编号
    @apiSuccess (Success) {String} response.docs.artists.description 歌手描述
    @apiSuccess (Success) {String} response.docs.artists.name 歌手名称
    @apiSuccess (Success) {String} response.docs.artists.gender 歌手性别
    @apiSuccess (Success) {String} response.docs.artists.rating 收听总数
    @apiSuccess (Success) {String} response.docs.artists.pinyinname 歌手拼音名称
    @apiSuccess (Success) {String} response.docs.artists.albumcount 歌手专辑数量

    @apiSuccess (Success) {String} response.docs.itemID 单曲编号列表

    @apiSuccess (Success) {String} response.docs.album 专辑列表
    @apiSuccess (Success) {String} response.docs.album.genre 专辑流派类型
    @apiSuccess (Success) {String} response.docs.album.cpInfo 专辑出版公司信息
    @apiSuccess (Success) {String} response.docs.album.cpInfo.contentProviderGroupID 专辑版权唱片公司分组编号
    @apiSuccess (Success) {String} response.docs.album.cpInfo.cpGroupName 专辑出版公司组名
    @apiSuccess (Success) {String} response.docs.album.cpInfo.cpGroupAltName 专辑出版公司组名更改名
    @apiSuccess (Success) {String} response.docs.album.cpInfo.cpName 专辑出版公司名称
    @apiSuccess (Success) {String} response.docs.album.cpInfo.cpCode 专辑出版公司代号
    @apiSuccess (Success) {String} response.docs.album.cpInfo.contentProviderID 专辑版权唱片公司ID
    @apiSuccess (Success) {String} response.docs.album.albumID 专辑编号
    @apiSuccess (Success) {String} response.docs.album.releasedate 专辑发布日期
    @apiSuccess (Success) {String} response.docs.album.translatename 专辑翻译名称
    @apiSuccess (Success) {String} response.docs.album.version 专辑版本
    @apiSuccess (Success) {String} response.docs.album.artists 专辑歌手
    @apiSuccess (Success) {String} response.docs.album.artists.artistname 专辑歌手名称
    @apiSuccess (Success) {String} response.docs.album.artists.artistID 专辑歌手编号
    @apiSuccess (Success) {String} response.docs.album.artists.artisttranslatename 专辑歌手翻译名称
    @apiSuccess (Success) {String} response.docs.album.artists.artistpinyinname 专辑歌手拼音名称
    @apiSuccess (Success) {String} response.docs.album.totaltrack 专辑总声音数量
    @apiSuccess (Success) {String} response.docs.album.imagePathMap 专辑图像路径图
    @apiSuccess (Success) {String} response.docs.album.imagePathMap.value 专辑图像路径图值
    @apiSuccess (Success) {String} response.docs.album.imagePathMap.key 专辑图像路径图键
    @apiSuccess (Success) {String} response.docs.album.explicit 专辑脏标
    @apiSuccess (Success) {String} response.docs.album.albumPriceCode 专辑价格代号
    @apiSuccess (Success) {String} response.docs.album.description 专辑描述
    @apiSuccess (Success) {String} response.docs.album.name 专辑名称
    @apiSuccess (Success) {String} response.docs.album.totaldisk 专辑总磁盘
    @apiSuccess (Success) {String} response.docs.album.rating 专辑收听总数
    @apiSuccess (Success) {String} response.docs.album.pinyinname 专辑拼音名称
    @apiSuccess (Success) {String} response.docs.album.languagetype 专辑语言类型
    @apiSuccess (Success) {String} response.docs.itemType 单曲类型
    @apiSuccess (Success) {String} musicinfo.numFound 查找数

    @apiSuccess (Success) {Object} responseHeader 响应头
    @apiSuccess (Success) {String} responseHeader.errorinfo 错误信息
    @apiSuccess (Success) {String} responseHeader.status 状态

    @apiSuccessExample {json} Success-Response:
    {
        "data": {
            "response": {
                "start": 0,
                "docs": [
                    {
                        "datainfo": {
                            "genre": "Pop",
                            "bitrates": [
                                "LRC-LRC",
                                "MP3-128K-FTD",
                                "MP3-192K-FTD",
                                "MP3-256K-FTD",
                                "MP3-320K-FTD"
                            ],
                            "releasedate": "2012-02-20",
                            "label": "海蝶音乐",
                            "exclusivity": 10,
                            "translatename": "唱响世界",
                            "version": "",
                            "duration": "04:39",
                            "side": 1,
                            "explicit": 0,
                            "description": "",
                            "name": "唱响世界",
                            "tracknumber": 1,
                            "rating": 42399,
                            "cpItemID": "GDOBX0920089_FTD",
                            "pinyinname": "Chang Xiang Shi Jie",
                            "languagetype": "MAN",
                            "priceCode": ""
                        },
                        "artists": [
                            {
                                "genre": "Pop",
                                "region": "SG",
                                "birthday": "1981-08-17",
                                "itemcount": 13,
                                "translatename": "Hong Jun Yang",
                                "imagePathMap": [
                                    {
                                        "value": "/artist/001/16121-JPG-240X240-ARTIST.jpg",
                                        "key": "JPG-240X240-ARTIST"
                                    }
                                ],
                                "artistID": 16121,
                                "description": "洪俊扬，1981年8月17日出生，毕业于新加坡国立大学(National University of Singapore)，2006年5月26日发行第一张专辑《独角兽》。2010年2月1日，洪俊扬与杜蕙甹举行婚礼。",
                                "name": "洪俊扬",
                                "gender": "Male",
                                "rating": 33882,
                                "pinyinname": "Hong Jun Yang",
                                "albumcount": 2
                            }
                        ],
                        "itemID": 66664,
                        "album": {
                            "genre": "Pop",
                            "cpInfo": [
                                {
                                    "contentProviderGroupID": 33,
                                    "cpGroupName": "海蝶音乐",
                                    "cpGroupAltName": "海蝶音乐",
                                    "cpName": "海蝶音乐",
                                    "cpCode": "海蝶音乐",
                                    "contentProviderID": 4456
                                }
                            ],
                            "albumID": 11399,
                            "releasedate": "2012-02-20",
                            "translatename": "海蝶夯之唱响世界",
                            "version": "",
                            "artists": [
                                {
                                    "artistname": "华语群星",
                                    "artistID": 94146,
                                    "artisttranslatename": "华语群星",
                                    "artistpinyinname": "Hua Yu Qun Xing"
                                }
                            ],
                            "totaltrack": 30,
                            "imagePathMap": [
                                {
                                    "value": "/album/001/11399-JPG-240X240-ALBUM.jpg",
                                    "key": "JPG-240X240-ALBUM"
                                }
                            ],
                            "explicit": 0,
                            "albumPriceCode": -1,
                            "name": "海蝶夯之唱响世界",
                            "totaldisk": 2,
                            "rating": 964491,
                            "pinyinname": "Hai Die Ben Zhi Chang Xiang Shi Jie",
                            "languagetype": "MAN"
                        },
                        "itemType": "FTD"
                    },
                ],
                "numFound": 2
            },
            "responseHeader": {
                "errorinfo": "",
                "status": "00"
            }
        }
    }

    @apiError (Error) 31014 No artist id in the request params!

    @apiErrorExample {json} Error-Response:
    {
        "data": "No artist id in the request params!"
    }
    """


def get_all(isamAppliance, check_mode=False, force=False):
    """
    Retrieving a list of all SMTP server connections
    """
    return isamAppliance.invoke_get(
        'Retrieving a list of all SMTP server connections',
        '/mga/server_connections/smtp/v1')


def ideal_gas(pressure, pressure_height, hub_height, temperature_hub_height):
    """
    Calculates the density of air at hub height using the ideal gas equation.

    This function is carried out when the parameter `density_model` of an
    instance of the :class:`~.modelchain.ModelChain` class is 'ideal_gas'.

    Parameters
    ----------
    pressure : :pandas:`pandas.Series<series>` or numpy.array
        Air pressure in Pa.
    pressure_height : float
        Height in m for which the parameter `pressure` applies.
    hub_height : float
        Hub height of wind turbine in m.
    temperature_hub_height : :pandas:`pandas.Series<series>` or numpy.array
        Air temperature at hub height in K.

    Returns
    -------
    :pandas:`pandas.Series<series>` or numpy.array
        Density of air at hub height in kg/m³.
        Returns a pandas.Series if one of the input parameters is a
        pandas.Series.

    Notes
    -----
    The following equations are used [1]_ [2]_ [3]_:

    .. math:: \\rho_{hub}=p_{hub}/ (R_s T_{hub})

    and [4]_:

    .. math:: p_{hub}=\\left(p/100-\\left(h_{hub}-h_{p,data}\\right)\\cdot
              \\frac{1}{8}\\right)\\cdot 100

    with:
        T: temperature [K], :math:`\\rho`: density [kg/m³], p: pressure [Pa]

    :math:`h_{p,data}` is the height of the measurement or model data for
    pressure, :math:`R_s` is the specific gas constant of dry air
    (287.058 J/(kg*K)) and :math:`p_{hub}` is the pressure at hub height
    :math:`h_{hub}`.

    References
    ----------
    .. [1] Ahrendts J., Kabelac S.: "Das Ingenieurwissen - Technische
            Thermodynamik". 34. Auflage, Springer-Verlag, 2014, p. 23
    .. [2] Biank, M.: "Methodology, Implementation and Validation of a
            Variable Scale Simulation Model for Windpower based on the
            Georeferenced Installation Register of Germany". Master's Thesis
            at RLI, 2014, p. 57
    .. [3] Knorr, K.: "Modellierung von raum-zeitlichen Eigenschaften der
            Windenergieeinspeisung für wetterdatenbasierte
            Windleistungssimulationen". Universität Kassel, Diss., 2016, p. 97
    .. [4] Deutscher Wetterdienst:
            http://www.dwd.de/DE/service/lexikon/begriffe/D/Druckgradient_pdf.pdf?__blob=publicationFile&v=4

    """
    return (pressure / 100 - (hub_height - pressure_height) * 1 / 8) * 100 / (
        287.058 * temperature_hub_height)


def Language():
    """
        Returns the language of this translation - same as DC
        """


def _delay():
    """can be implemented if the SPI data rate has to be lowered"""
    pass


def str_to_str(string):
    """Yank leading/trailing whitespace and quotation, along with newlines."""
    s = string.strip()
    if len(s) > 1 and s[0] in '"\'' and s[0] == s[-1]:
        s = s[1:-1]
    return s.replace('\n', ' ').replace('\t', ' ')


def refresh(file_path):
    """
    Wipes a file of content
    :param file_path: file path of file to wipe
    :return: none
    """
    f = open(str(file_path), 'w')
    f.write('')
    f.close()


@staticmethod
def _return_id_type(resp):
    """
        Check dict for ID and return ID and type if present.

        :param resp: (dict) Response from NetworkGroup, NetworkAddress or FQDNS GET
        :return: id (str), type (str) or None None
        """
    if 'id' in resp.keys():
        return resp['id'], resp['type']
    else:
        return None, None


def create():
    """Return a new `IDatabase`.

        The database will be initialized and all migrations will be loaded.

        :return: A new database.
        :rtype: IDatabase
        """


@classmethod
def table_name_col(cls):
    """
        Return the name of the metadata column used to store the feature table
        name.
        """
    return 'f_table_name'


def layers_ui(layers, layout):
    """ Turn a list of booleans + a list of names into a layer UI.
    """
    code = """
class RigLayers(bpy.types.Panel):
    bl_space_type = 'VIEW_3D'
    bl_region_type = 'UI'
    bl_label = "Rig Layers"
    bl_idname = rig_id + "_PT_rig_layers"

    @classmethod
    def poll(self, context):
        try:
            return (context.active_object.data.get("rig_id") == rig_id)
        except (AttributeError, KeyError, TypeError):
            return False

    def draw(self, context):
        layout = self.layout
        col = layout.column()
"""
    rows = {}
    for i in range(28):
        if layers[i]:
            if layout[i][1] not in rows:
                rows[layout[i][1]] = []
            rows[layout[i][1]] += [(layout[i][0], i)]
    keys = list(rows.keys())
    keys.sort()
    for key in keys:
        code += """
        row = col.row()
"""
        i = 0
        for l in rows[key]:
            if i > 3:
                code += '\n        row = col.row()\n'
                i = 0
            code += (
                """        row.prop(context.active_object.data, 'layers', index=%s, toggle=True, text='%s')
"""
                 % (str(l[1]), l[0]))
            i += 1
    code += """
        row = col.row()"""
    code += """
        row.separator()"""
    code += """
        row = col.row()"""
    code += """
        row.separator()
"""
    code += """
        row = col.row()
"""
    code += """        row.prop(context.active_object.data, 'layers', index=28, toggle=True, text='Root')
"""
    return code


@staticmethod
def email_users(users, subject, text_body, html_body=None, sender=None, cc=
    None, bcc=None, configuration=None, **kwargs):
    """Email a list of users

        Args:
            users (List[User]): List of users in To address
            subject (str): Email subject
            text_body (str): Plain text email body
            html_body (str): HTML email body
            sender (Optional[str]): Email sender. Defaults to SMTP username.
            cc (Optional[List[User]]: List of users to cc. Defaults to None.
            bcc (Optional[List[User]]: List of users to bcc. Defaults to None.
            configuration (Optional[Configuration]): HDX configuration. Defaults to configuration of first user in list.
            **kwargs: See below
            mail_options (List): Mail options (see smtplib documentation)
            rcpt_options (List): Recipient options (see smtplib documentation)

        Returns:
            None
        """
    if not users:
        raise ValueError('No users supplied')
    recipients = list()
    for user in users:
        recipients.append(user.data['email'])
    ccemails = list()
    for user in cc:
        ccemails.append(user.data['email'])
    bccemails = list()
    for user in bcc:
        bccemails.append(user.data['email'])
    if configuration is None:
        configuration = users[0].configuration
    configuration.emailer().send(recipients, subject, text_body, html_body=
        html_body, sender=sender, cc=ccemails, bcc=bccemails, **kwargs)


def clear_caches():
    """Jinja2 keeps internal caches for environments and lexers.  These are
    used so that Jinja2 doesn't have to recreate environments and lexers all
    the time.  Normally you don't have to care about that but if you are
    measuring memory consumption you may want to clean the caches.
    """
    from jinja2.environment import _spontaneous_environments
    from jinja2.lexer import _lexer_cache
    _spontaneous_environments.clear()
    _lexer_cache.clear()


def render_pep440_pre(pieces):
    """TAG[.post.devDISTANCE] -- No -dirty.

    Exceptions:
    1: no tags. 0.post.devDISTANCE
    """
    if pieces['closest-tag']:
        rendered = pieces['closest-tag']
        if pieces['distance']:
            rendered += '.post.dev%d' % pieces['distance']
    else:
        rendered = '0.post.dev%d' % pieces['distance']
    return rendered


@staticmethod
def get_volume_attached_hostname(device_info):
    """Parse a hostname from a storage group ID.

        :param device_info: the device info dict
        :return: str -- the attached hostname
        """
    try:
        sg_id = device_info.get('storageGroupId')[0]
        return sg_id.split('-')[1]
    except IndexError:
        return None


def to_ascii(text):
    """
    Force a string or other to ASCII text ignoring errors.

    Parameters
    -----------
    text : any
      Input to be converted to ASCII string

    Returns
    -----------
    ascii : str
      Input as an ASCII string
    """
    if hasattr(text, 'encode'):
        return text.encode('ascii', errors='ignore').decode('ascii')
    elif hasattr(text, 'decode'):
        return text.decode('ascii', errors='ignore')
    return str(text)


def _set_progress(params, name: str, index: int, value: float):
    """Adjust the Disease.progress parameter"""
    params.disease_params.progress[index] = value


def content_layout():
    """Returns the content HTML layout.
        """


def split_normals():
    """Split custom normals of selected vertices

    """
    pass


@classmethod
def de_json(cls, json_type):
    """
        Returns an instance of this class from the given json dict or string.

        This function must be overridden by subclasses.
        :return: an instance of this class created from the given json dict or string.
        """
    raise NotImplementedError


@staticmethod
def queue_compare(schedule1, schedule2):
    """
        Compares two schedules for sorting queued nodes.

        :param enarksh.controller.Schedule.Schedule schedule1: Schedule 1.
        :param enarksh.controller.Schedule.Schedule schedule2: Schedule 2.

        :rtype:
        """
    return schedule1.get_schedule_load() - schedule2.get_schedule_load()


def std(array, *args, **kwargs):
    """
    Computes the standard deviation.

    See Also
    --------
    Array.std
    """
    return array.std(*args, **kwargs)


def transform_axis_target():
    """Interactively point cameras and lights to a location (Ctrl translates)

    """
    pass


def _allow_join_condition(statement):
    """
    Tests if a join condition should be suggested

    We need this to avoid bad suggestions when entering e.g.
        select * from tbl1 a join tbl2 b on a.id = <cursor>
    So check that the preceding token is a ON, AND, or OR keyword, instead of
    e.g. an equals sign.

    :param statement: an sqlparse.sql.Statement
    :return: boolean
    """
    if not statement or not statement.tokens:
        return False
    last_tok = statement.token_prev(len(statement.tokens))[1]
    return last_tok.value.lower() in ('on', 'and', 'or')


def mock_error_owwidget(node, message):
    """
    Create a mock OWWidget instance for `node`.

    :type node: SchemeNode

    """
    from Orange.OrangeWidgets.OWWidget import _DummyOWWidget
    widget = _DummyOWWidget()
    widget.widgetInfo = node.description
    widget._settingsFromSchema = node.properties
    for link in node.description.inputs:
        handler = link.handler
        if handler.startswith('self.'):
            _, handler = handler.split('.', 1)
        setattr(widget, handler, lambda *args: None)
    widget.setBlocking(True)
    widget.setErrorMessage(message)
    return widget


def undo_filter_sub(filter_unit, scanline, previous, result):
    """Undo sub filter."""
    ai = 0
    for i in range(filter_unit, len(result)):
        x = scanline[i]
        a = result[ai]
        result[i] = x + a & 255
        ai += 1


def capture_screen(surface, rect=None):
    """Returns a copy of the surface <surface>, with restriction <rect>
        (None means the whole surface)"""
    if not rect:
        rect = surface.get_rect()
    return surface.copy().subsurface(rect).convert()


def _segments_diff(segments, havesegs, query=True):
    """Return a diff of two `SegmentList`, and determine whether to query for
    new data
    """
    new = segments - havesegs
    query &= abs(new) != 0
    return new, query


@classmethod
def _from_dict(cls, _dict):
    """Initialize a Contact object from a json dictionary."""
    args = {}
    if 'name' in _dict:
        args['name'] = _dict.get('name')
    if 'role' in _dict:
        args['role'] = _dict.get('role')
    return cls(**args)


def windows_taskbar_compat():
    """Enable icon and taskbar grouping for Windows 7+"""
    import ctypes
    ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(
        u'pyblish.qml')


@staticmethod
def sanitizePartialBeams(beamsList):
    """
        It is possible at a late stage to have beams that only consist of partials
        or beams with a 'start' followed by 'partial/left' or possibly 'stop' followed
        by 'partial/right'; beams entirely consisting of partials are removed
        and the direction of irrational partials is fixed.
        """
    for i in range(len(beamsList)):
        if beamsList[i] is None:
            continue
        allTypes = beamsList[i].getTypes()
        if ('start' not in allTypes and 'stop' not in allTypes and
            'continue' not in allTypes):
            beamsList[i] = None
            continue
        hasStart = False
        hasStop = False
        for b in beamsList[i].beamsList:
            if b.type == 'start':
                hasStart = True
                continue
            if b.type == 'stop':
                hasStop = True
                continue
            if hasStart and b.type == 'partial' and b.direction == 'left':
                b.direction = 'right'
            elif hasStop and b.type == 'partial' and b.direction == 'right':
                b.direction = 'left'
    return beamsList


@staticmethod
def interfaces_roles_change_and_tree_remains_active(interface:
    'TreeInterfaceUpstream') ->None:
    """
        Interfaces roles change (NonRoot->Root) AND
        Tree was Active and remains Active
        """
    interface.logger.debug('interfaces_roles_change_and_tree_remains_active')
    interface.send_i_am_no_longer_upstream()
    interface.send_my_interest()


def gen_number_map() ->dict:
    """
    Returns base number mapping including combinations like 4 -> IV and 9 -> IX, etc.
    """
    mapping = {(1000): 'M', (500): 'D', (100): 'C', (50): 'L', (10): 'X', (
        5): 'V', (1): 'I', (0): 'N'}
    for exponent in range(3):
        for num in (4, 9):
            power = 10 ** exponent
            mapping[num * power] = mapping[1 * power] + mapping[(num + 1) *
                power]
    return mapping


def parse_image_meta_graph(meta):
    """Parses a tensor that contains image attributes to its components.
    See compose_image_meta() for more details.

    meta: [batch, meta length] where meta length depends on NUM_CLASSES

    Returns a dict of the parsed tensors.
    """
    image_id = meta[:, (0)]
    original_image_shape = meta[:, 1:4]
    image_shape = meta[:, 4:7]
    window = meta[:, 7:11]
    scale = meta[:, (11)]
    active_class_ids = meta[:, 12:]
    a_meta = {'image_id': image_id, 'original_image_shape':
        original_image_shape, 'image_shape': image_shape, 'window': window,
        'scale': scale, 'active_class_ids': active_class_ids}
    return a_meta


def compute_diffs(df_dict, df_str, gridclimname1, gridclimname2, prefix1,
    prefix2='meanmonth', comp_dict=None):
    """
    Compute difference between monthly means (e.g,. Temp) for two different gridded datasets (e.g., Liv, WRF)
    """
    if isinstance(comp_dict, type(None)):
        comp_dict = {}
    for each1 in prefix1:
        for each2 in prefix2:
            diffs = df_dict['_'.join([each2, each1, gridclimname1])] - df_dict[
                '_'.join([each2, each1, gridclimname2])]
            comp_dict['_'.join([str(each1), df_str])] = diffs
    return comp_dict


def flatten_list(list_of_list):
    """Input a list of list, return a list that all items are in a list.

    Parameters
    ----------
    list_of_list : a list of list

    Examples
    --------
    >>> tl.utils.flatten_list([[1, 2, 3],[4, 5],[6]])
    [1, 2, 3, 4, 5, 6]

    """
    return sum(list_of_list, [])


def rh2s(raw_hash):
    """
    Encode raw transaction hash to HEX string with bytes order change

    :param raw_hash: transaction hash in bytes string.
    :return:  HEX encoded string.
    """
    return raw_hash[::-1].hex()


@staticmethod
def unserialize(channel, payload_as_buffer):
    """
        Unserialize from a buffer.
        Buffer starts at frame's own payload - type, channel and size was already obtained.
        Payload does not contain FRAME_EMD.
        AMQPHeartbeatFrame does not have to implement this.
        """
    raise NotImplementedError('Override me')


@classmethod
def getConfigSections(cls):
    """Only needs to be overriden if has additional ini sections."""
    return ['lightnovelgate.com', cls.getSiteDomain()]


def standardize(raw):
    """Standardize channel positions and names.

    Parameters
    ----------
    raw : instance of Raw
        The raw data to standardize. Operates in-place.
    """
    rename = dict()
    for name in raw.ch_names:
        std_name = name.strip('.')
        std_name = std_name.upper()
        if std_name.endswith('Z'):
            std_name = std_name[:-1] + 'z'
        if std_name.startswith('FP'):
            std_name = 'Fp' + std_name[2:]
        rename[name] = std_name
    raw.rename_channels(rename)


@classmethod
def authenticate(cls, args):
    """
        Classmethod to authenticate a connection. Should return an
        Authenticator instance if the connection is authenticated or None if not.

        `args` contains the auth_token from the client library (can contain the
        connection identifier and/or additional data).
        """
    return cls(args)


def nt_pos_shift(nt_pos, AAshift):
    """
		Modify nucleotide positions in our alignment to actual positions in the real wild-type sequence.

		nt_pos : base number of wild type sequence start at 1, not python 0
		AAshift : the residue corresponding to the wild type base starting at 1
	"""
    if nt_pos < 1:
        raise Exception('Bases provided must start at 1')
    return nt_pos + (AAshift - 1) * 3


def browser_exceptions():
    """ Activates display of runtime exceptions in the brower """
    global print_exceptions
    print_exceptions = not print_exceptions


def apply_format(data_frame, column_names, format_method):
    """Apply a formatting function to a DataFrame column and return.

    Simplify applying format modifications to the data stored in columns
    of `data_frame`. Check if the parameters are of the right type, apply
    `format_method` to the columns of `data_frame` whose labels are passed
    in `column names`. Return the DataFrame with the applied changes.

    Parameters
    ----------
    data_frame : pandas.DataFrame
        DataFrame containing the data to be modified.
    column_names : list
        List of string labels of columns in `data_frame` to be modified.
    format_method : function
        Function to be applied to the columns of `data_frame`, whose labels
        are listed in `column_names`.

    Returns
    -------
    data_frame : pandas.DataFrame
        The passed in DataFrame with the formatting changes applied to
        its columns.

    See Also
    --------
    pandas.apply

    Examples
    --------
    >>> data = pd.read_csv("data.csv")
    >>> print(data[['Wage']][0:3]) #print first few lines
        Wage
    0  €565K
    1  €405K
    2  €290K
    >>> data = apply_format(data, ['Wage'], money_format)
    >>> print(data[['Wage']][0:3])
       Wage
    0   565
    1   405
    2   290
    """
    for column in column_names:
        if isinstance(column, str) and column in data_frame and callable(
            format_method):
            data_frame.loc[:, (column)] = data_frame[column].apply(
                format_method)
    return data_frame


def set_workflow(ctype_or_obj, workflow):
    """Sets the workflow for passed content type or object. See the specific
    methods for more information.

    **Parameters:**

    workflow
        The workflow which should be set to the object or model.

    ctype_or_obj
        The content type or the object to which the passed workflow should be
        set. Can be either a ContentType instance or any Django model
        instance.
    """
    return workflow.set_to(ctype_or_obj)


def printH5(h5):
    """
    Print info about HDF5 data set (requires h5py)

    Parameters
    ----------
    h5
        HDF5 Dataset loaded with h5py
    """
    print('------ ', h5.filename, ' ------')
    ks = h5.keys()
    for key in ks:
        print(h5[key])
    for item in h5.attrs.keys():
        print(item + ':', h5.attrs[item])


@classmethod
def from_fractional_odds_on(cls, numerator=0, denominator=None):
    """Create a probability from fractional odds on."""
    n, d = cls._format_fractional(numerator, denominator)
    return cls(n, n + d)


def _get_a_kl_PE(UB):
    """Return the 4×4 `A_kl` coefficient matrix (:class:`qutip.Qobj`)
    for the perfect-entanglers functional, for a given gate `UB` in the Bell
    basis.
    """
    raise NotImplementedError()


def scan_setup_py():
    """Validate the contents of setup.py against Versioneer's expectations."""
    found = set()
    setters = False
    errors = 0
    with open('setup.py', 'r') as f:
        for line in f.readlines():
            if 'import versioneer' in line:
                found.add('import')
            if 'versioneer.get_cmdclass()' in line:
                found.add('cmdclass')
            if 'versioneer.get_version()' in line:
                found.add('get_version')
            if 'versioneer.VCS' in line:
                setters = True
            if 'versioneer.versionfile_source' in line:
                setters = True
    if len(found) != 3:
        print('')
        print('Your setup.py appears to be missing some important items')
        print('(but I might be wrong). Please make sure it has something')
        print('roughly like the following:')
        print('')
        print(' import versioneer')
        print(' setup( version=versioneer.get_version(),')
        print('        cmdclass=versioneer.get_cmdclass(),  ...)')
        print('')
        errors += 1
    if setters:
        print("You should remove lines like 'versioneer.VCS = ' and")
        print("'versioneer.versionfile_source = ' . This configuration")
        print('now lives in setup.cfg, and should be removed from setup.py')
        print('')
        errors += 1
    return errors


def safe_list_get(l, index, default=None):
    """
    Get value for index in l. If index is out of range, return default
    instead.
    """
    try:
        return l[index]
    except IndexError:
        return default


@classmethod
def _clone(cls, source, target, branch=None):
    """Clone repository."""
    raise NotImplementedError()


def render_git_describe(pieces):
    """TAG[-DISTANCE-gHEX][-dirty].

    Like 'git describe --tags --dirty --always'.

    Exceptions:
    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
    """
    if pieces['closest-tag']:
        rendered = pieces['closest-tag']
        if pieces['distance']:
            rendered += '-%d-g%s' % (pieces['distance'], pieces['short'])
    else:
        rendered = pieces['short']
    if pieces['dirty']:
        rendered += '-dirty'
    return rendered


def firewall_rule_update(client, firewall_rule_name, server_name,
    resource_group_name, start_ip_address=None, end_ip_address=None):
    """
    Updates a firewall rule.
    """
    instance = client.get(firewall_rule_name=firewall_rule_name,
        server_name=server_name, resource_group_name=resource_group_name)
    return client.create_or_update(firewall_rule_name=firewall_rule_name,
        server_name=server_name, resource_group_name=resource_group_name,
        start_ip_address=start_ip_address or instance.start_ip_address,
        end_ip_address=end_ip_address or instance.end_ip_address)


def indentation(string, indent, count=1):
    """Indents a string.

    Keyword arguments:
    string -- The string you want to indent.
    indent -- The string to use for the indent.
    count -- How many times you want indent repeated.
    """
    if type(string) is not str or type(indent) is not str:
        raise TypeError('Should be string')
    if type(count) is not int or count is None:
        raise TypeError('count should be Integer')
    if count == 0:
        return string
    splitted = string.split('\n')
    for i in range(len(splitted)):
        splitted[i] = count * indent + splitted[i]
    return '\n'.join(splitted)


def find_server(servers, fd):
    """ Find matching ``server.server_socket`` for given file descriptor. """
    for server in servers:
        if fd == server.server_socket.fileno():
            return server


def get_org_wallet_name(orgname):
    """
    Determine wallet name based on an organization name.
    """
    wallet_name = orgname.replace('@', '_')
    wallet_name = wallet_name.replace('.', '_')
    wallet_name = wallet_name.replace(' ', '_')
    return 'o_{}'.format(wallet_name).lower()


def _nanmean_ddof_object(ddof, value, axis=None, dtype=None, **kwargs):
    """ In house nanmean. ddof argument will be used in _nanvar method """
    from .duck_array_ops import count, fillna, _dask_or_eager_func, where_method
    valid_count = count(value, axis=axis)
    value = fillna(value, 0)
    if dtype is None and value.dtype.kind == 'O':
        dtype = value.dtype if value.dtype.kind in ['cf'] else float
    data = _dask_or_eager_func('sum')(value, axis=axis, dtype=dtype, **kwargs)
    data = data / (valid_count - ddof)
    return where_method(data, valid_count != 0)


def _convert_value(value, conversions, default):
    """Converts a value using a set of value mappings.

  Args:
    value: The value to convert.
    conversions: A dict giving the desired output for each of a set of possible
      input values.
    default: The value to return if the input value is not one of the ones
      listed in "conversions".

  Returns:
    The converted value.
  """
    return conversions.get(value, default)


def calculate_overlap():
    """Calculate the pairwise overlap between all collections."""
    raise NotImplementedError


def decode_response(client_message, to_object=None):
    """ Decode response from client message"""
    parameters = dict(response=None)
    parameters['response'] = client_message.read_bool()
    return parameters


def interpret_none(bb, context=None):
    """Convert a raw None value to a boolean.

    That is, unconditionally returns True.

    """
    return True


def is_number(ch: str):
    """Check if given code is a number"""
    return ch.isdecimal()


def dgl_graph_compact(*graph_data, **kwargs):
    """This operator compacts a CSR matrix generated by
    dgl_csr_neighbor_uniform_sample and dgl_csr_neighbor_non_uniform_sample.
    The CSR matrices generated by these two operators may have many empty
    rows at the end and many empty columns. This operator removes these
    empty rows and empty columns.

    Example:

       .. code:: python

      shape = (5, 5)
      data_np = np.array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20], dtype=np.int64)
      indices_np = np.array([1,2,3,4,0,2,3,4,0,1,3,4,0,1,2,4,0,1,2,3], dtype=np.int64)
      indptr_np = np.array([0,4,8,12,16,20], dtype=np.int64)
      a = mx.nd.sparse.csr_matrix((data_np, indices_np, indptr_np), shape=shape)
      seed = mx.nd.array([0,1,2,3,4], dtype=np.int64)
      out = mx.nd.contrib.dgl_csr_neighbor_uniform_sample(a, seed, num_args=2, num_hops=1,
              num_neighbor=2, max_num_vertices=6)
      subg_v = out[0]
      subg = out[1]
      compact = mx.nd.contrib.dgl_graph_compact(subg, subg_v,
              graph_sizes=(subg_v[-1].asnumpy()[0]), return_mapping=False)

      compact.asnumpy()
      array([[0, 0, 0, 1, 0],
             [2, 0, 3, 0, 0],
             [0, 4, 0, 0, 5],
             [0, 6, 0, 0, 7],
             [8, 9, 0, 0, 0]])

    Defined in src/operator/contrib/dgl_graph.cc:L1582
    This function support variable length of positional input.

    Parameters
    ----------
    graph_data : Symbol[]
        Input graphs and input vertex Ids.
    return_mapping : boolean, required
        Return mapping of vid and eid between the subgraph and the parent graph.
    graph_sizes : tuple of <long>, required
        the number of vertices in each graph.

    name : string, optional.
        Name of the resulting symbol.

    Returns
    -------
    Symbol
        The result symbol.
    """
    return 0,


@classmethod
def failed(cls, val):
    """
        Create an instance of this class set to the ``failed`` tag with value
        ``val``.

        :param str val:
        :rtype: MembersAddJobStatus
        """
    return cls('failed', val)


def convert_hex_to_url(hex_value):
    """
    Converts a hex value to a url for an image of that value

    Returns:
        url (str): A url referencing an image of the given hex value
    """
    return 'https://dummyimage.com/350x200.png/{0}/{0}'.format(hex_value)


def _not_implemented(*args):
    """An error raising function that can take any amount of parameters.

    Parameters
    ----------
    *args
        Variable length argument list, it does not matter what or how many
        parameters you use. Does not accept keyword arguments.

    Raises
    ------
    NotImplementedError
        This error will always be raised.

    """
    raise NotImplementedError


@classmethod
def _get_type(cls, ordinal):
    """
        Gets the ``HLLType`` for the specified ordinal.

        :param int ordinal: the ordinal whose type is desired
        :returns: the type for the specified ordinal. This will never be ``None``.
        :rtype: HLLType
        """
    if ordinal < 0 or ordinal >= len(cls.TYPE_ORDINALS):
        raise ValueError('Invalid type ordinal {}. Only 0-{} inclusive allowed'
            .format(ordinal, len(cls.TYPE_ORDINALS) - 1))
    return cls.TYPE_ORDINALS[ordinal]


def convert_bytes(num):
    """
    this function will convert bytes to MB.... GB... etc
    """
    for x in ['bytes', 'KB', 'MB', 'GB', 'TB']:
        if num < 1024.0:
            return '%3.1f %s' % (num, x)
        num /= 1024.0


def get_share_counter_wrong():
    """For mutable files, return the number of shares for versions other
        than the 'best' one (which is defined as being the recoverable
        version with the highest sequence number, then the highest roothash).
        These are either leftover shares from an older version (perhaps on a
        server that was offline when an update occurred), shares from an
        unrecoverable newer version, or shares from an alternate current
        version that results from an uncoordinated write collision. For a
        healthy file, this will equal 0. For immutable files, this will
        always equal 0."""


def to_fixed_width(n, max_width, allow_overflow=True, do_round=True):
    """Format a float to a fixed-width string.

    Args:
      - n (float):              number to format
      - max_width (int):        width of output string
      - allow_overflow (bool):  allow the result to be larger than max_width for
                                large n? Raises OverflowError if False
      - do_round (bool):        Round final digit?
    Returns:
        (str)   string representation of n, either padded or rounded to have
                max_width characters
    """
    if do_round:
        for i in range(max_width - 2, -1, -1):
            str0 = '{:.{}f}'.format(n, i)
            if len(str0) <= max_width:
                break
    else:
        str0 = '{:.42f}'.format(n)
        int_part_len = str0.index('.')
        if int_part_len <= max_width - 2:
            str0 = str0[:max_width]
        else:
            str0 = str0[:int_part_len]
    if not allow_overflow and len(str0) > max_width:
        raise OverflowError(
            'Impossible to represent in fixed-width non-scientific format')
    return str0


@staticmethod
def reshape(tensor, newshape):
    """Gives a new shape to a tensor without changing its data.

        Parameters
        ----------
        tensor : tl.tensor
        newshape : int or tuple of ints
            The new shape should be compatible with the original shape. If an
            integer, then the result will be a 1-D tensor of that length.

        Returns
        -------
        tensor
        """
    raise NotImplementedError


def is_multicast(mac):
    """Whether a MAC address is IPV4/V6 multicast address.

    See https://en.wikipedia.org/wiki/Multicast_address#Ethernet

    ARgs:
        mac (str): MAC address

    Returns:
        bool

    >>> is_multicast('01:80:C2:00:00:08')
    True

    """
    octet = int(mac.split(':')[0], base=16)
    return octet & 1 > 0


def tail(f, window=1):
    """
    Returns the last `window` lines of file `f` as a list of bytes.
    """
    if window == 0:
        return b''
    BUFSIZE = 1024
    f.seek(0, 2)
    end = f.tell()
    nlines = window + 1
    data = []
    while nlines > 0 and end > 0:
        i = max(0, end - BUFSIZE)
        nread = min(end, BUFSIZE)
        f.seek(i)
        chunk = f.read(nread)
        data.append(chunk)
        nlines -= chunk.count(b'\n')
        end -= nread
    return b'\n'.join(b''.join(reversed(data)).splitlines()[-window:])


def get_interpob(model):
    """Retrieves the interpolation dictionary from model.

    Different nufft objects use different interpolation objects. This function
    only extracts the minimum amount necessary for sparse matrix
    precomputation.

    Args:
        model (TorchKbNufft object): A TorchKbNufft object with attributes for
            forming a KbNufft interpolation dictionary.

    Returns:
        dict: A dictionary with interpolation parameters.
    """
    interpob = dict()
    interpob['table'] = []
    for i in range(len(model.table)):
        interpob['table'].append(getattr(model, 'table_tensor_' + str(i)))
    interpob['grid_size'] = model.grid_size_tensor
    interpob['numpoints'] = model.numpoints_tensor
    interpob['table_oversamp'] = model.table_oversamp_tensor
    return interpob


def render_git_describe_long(pieces):
    """TAG-DISTANCE-gHEX[-dirty].

    Like 'git describe --tags --dirty --always -long'.
    The distance/hash is unconditional.

    Exceptions:
    1: no tags. HEX[-dirty]  (note: no 'g' prefix)
    """
    if pieces['closest-tag']:
        rendered = pieces['closest-tag']
        rendered += '-%d-g%s' % (pieces['distance'], pieces['short'])
    else:
        rendered = pieces['short']
    if pieces['dirty']:
        rendered += '-dirty'
    return rendered


def _create_kaldi_ark_scp_file(ark_path, scp_path, fileid):
    """ create a scp file to access the ark file from kaldi
    Arguments:
    ark_path -- the path to the ark file
    scp_path -- where the scp file will be written
    fileid -- identifiant of the file
    """
    scp_content = '{} {}'.format(fileid, ark_path)
    with open(scp_path, 'w') as f:
        f.write(scp_content)


def contextfunction(f):
    """This decorator can be used to mark a function or method context callable.
    A context callable is passed the active :class:`Context` as first argument when
    called from the template.  This is useful if a function wants to get access
    to the context or functions provided on the context object.  For example
    a function that returns a sorted list of template variables the current
    template exports could look like this::

        @contextfunction
        def get_exported_names(context):
            return sorted(context.exported_vars)
    """
    f.contextfunction = True
    return f


def Serialize(item):
    """
    Serializes an item into a bytearray
    """
    pass


def load_Xlib_XK(finder, module):
    """the Xlib.XK module implicitly loads some keysymdef modules; make sure
       this happens."""
    finder.IncludeModule('Xlib.keysymdef.miscellany')
    finder.IncludeModule('Xlib.keysymdef.latin1')


def build_shutdown_url(base):
    """
    Builds the shutdown endpoint for the specified port

    :param port: integer string for a D-Tale process's port
    :type port: str
    :return: URL string of the shutdown endpoint for the current server and port passed
    """
    return '{}/shutdown'.format(base)


@classmethod
def identity(cls, dim):
    """Returns a dim x dim identity matrix."""
    if dim < 1:
        raise ValueError(
            'Identity matrix must have positive size, %d not allowed.' % dim)
    return cls._matrix_alloc_identity(dim)


def get_ident():
    """Dummy implementation of _thread.get_ident().

    Since this module should only be used when _threadmodule is not
    available, it is safe to assume that the current process is the
    only thread.  Thus a constant can be safely returned.
    """
    return -1


def map1(func, *xs):
    """Apply each argument to a single-argument function and
       return a C{tuple} of results.

       @arg func: Function to apply (C{callable}).
       @arg xs: Arguments to apply (C{any positional}).

       @return: Function results (C{tuple}).
    """
    return tuple(map(func, xs))


def cloud_sd_func(x, a):
    """Function for fitting the standard deviation of wet and dry cloud to the
    mean of wet or dry cloud

    This function returns y with

    .. math::
        y = a^2 x (1 - x)

    Parameters
    ----------
    x: np.ndarray
        The x input data
    a: float
        The parameter as mentioned in the equation above"""
    return a * a * x * (1 - x)


def file_length(file_path: str) ->int:
    """
    Returns the number of lines in a file
    """
    i = -1
    with open(file_path) as f:
        for i, l in enumerate(f):
            pass
    return i + 1


@classmethod
def using(cls, *options, **kwargs):
    """
        Convenience for calling update multiple times

        .. code-block:: python

            m = MergedOptions.using({"a": 1}, {"b": 2})

        is equivalent to:

        .. code-block:: python

            m = MergedOptions()
            m.update({"a": 1})
            m.update({"b": 2})

        Any kwargs given to ``using`` is passed into ``update`` for
        each provided dictionary.
        """
    prefix = kwargs.get('prefix')
    storage = kwargs.get('storage')
    converters = kwargs.get('converters')
    dont_prefix = kwargs.get('dont_prefix')
    ignore_converters = kwargs.get('ignore_converters')
    merged = cls(prefix=prefix, storage=storage, dont_prefix=dont_prefix,
        converters=converters, ignore_converters=ignore_converters)
    for opts in options:
        merged.update(opts, **kwargs)
    return merged


@classmethod
def chunk_bytes(cls, data):
    """
        Chunks some data up so it can be sent in reasonable size messages.
        Yields (chunk, last_chunk) tuples.
        """
    position = 0
    if not data:
        yield data, True
        return
    while position < len(data):
        yield data[position:position + cls.chunk_size
            ], position + cls.chunk_size >= len(data)
        position += cls.chunk_size


def valid_age(person, i, min_age):
    """Verify age requirement of person in given iteration."""
    return person['birth'] <= i - min_age and person['death'] >= i


def time_averaged_sound_intensity(intensity, axis=-1):
    """
    Time-averaged sound intensity :math:`\\mathbf{I}_T`.

    :param intensity: Sound intensity :math:`\\mathbf{i}`.
    :param axis: Axis.

    .. math:: \\mathbf{I}_T = \\frac{1}{T} \\int_{t_1}^{t_2} \\mathbf{i}(t)

    """
    return intensity.mean(axis=axis)


def round_to_fraction(val, res, decimals=None):
    """ round to closest resolution """
    if val is None:
        return 0.0
    if decimals is None and '.' in str(res):
        decimals = len(str(res).split('.')[1])
    return round(round(val / res) * res, decimals)


def render_pep440_old(pieces):
    """TAG[.postDISTANCE[.dev0]] .

    The ".dev0" means dirty.

    Eexceptions:
    1: no tags. 0.postDISTANCE[.dev0]
    """
    if pieces['closest-tag']:
        rendered = pieces['closest-tag']
        if pieces['distance'] or pieces['dirty']:
            rendered += '.post%d' % pieces['distance']
            if pieces['dirty']:
                rendered += '.dev0'
    else:
        rendered = '0.post%d' % pieces['distance']
        if pieces['dirty']:
            rendered += '.dev0'
    return rendered


def set_collision_class(cls):
    """Define qual é a classe encarregada de resolver colisões no lugar de
    FGAme.physics.Collision."""
    from FGAme.physics import collision_pairs
    collision_pairs.Collision = cls


@classmethod
def list(cls, options=None):
    """List available datacenters."""
    return cls.safe_call('hosting.datacenter.list', options or {})


@staticmethod
def _check_points(points):
    """
        Verifies that there are at least three points.

        Args:
            points (iterable): All of the points
        """
    if len(points) < 3:
        raise ValueError('"points" must be a list of 3 or more points')


def is_approved():
    """Return true if this object is versioned or contains
        versioned content that is approved.
        """


def copyVoltageKeywords(inhdr, outhdr, detector):
    """Copy keywords for high voltages from one header to another.

    This is for copying the high voltage keywords from the input extension
    header to the primary header of the csum file.

    Parameters
    ----------
    inhdr: pyfits Header object
        Input header.

    outhdr: pyfits Header object
        Output header.

    detector: {"FUV", "NUV"}
        Detector name.
    """
    if detector == 'FUV':
        outhdr['dethvla'] = inhdr.get('dethvla', -999.0)
        outhdr['dethvlb'] = inhdr.get('dethvlb', -999.0)
        outhdr['dethvca'] = inhdr.get('dethvca', -999.0)
        outhdr['dethvcb'] = inhdr.get('dethvcb', -999.0)
        outhdr['dethvna'] = inhdr.get('dethvna', -999.0)
        outhdr['dethvnb'] = inhdr.get('dethvnb', -999.0)
    elif detector == 'NUV':
        outhdr['dethvl'] = inhdr.get('dethvl', -999.0)
        outhdr['dethvc'] = inhdr.get('dethvc', -999.0)


def scale(val, src, dst):
    """
    Scale value from src range to dst range.
    If value outside bounds, it is clipped and set to
    the low or high bound of dst.

    Ex:
        scale(0, (0.0, 99.0), (-1.0, 1.0)) == -1.0
        scale(-5, (0.0, 99.0), (-1.0, 1.0)) == -1.0

    """
    if val < src[0]:
        return dst[0]
    if val > src[1]:
        return dst[1]
    return (val - src[0]) / (src[1] - src[0]) * (dst[1] - dst[0]) + dst[0]


def invisible_input(prompt='>>> '):
    """ Get raw input from a terminal without echoing the characters to
        the terminal, e.g. for password queries.

    """
    import getpass
    entry = getpass.getpass(prompt)
    if entry is None:
        raise KeyboardInterrupt
    return entry


def mod_var_name_decl(raw_module_name):
    """ function to get the last component of the module e.g.
        pyjamas.ui.DOM into the "namespace".  i.e. doing
        "import pyjamas.ui.DOM" actually ends up with _two_
        variables - one pyjamas.ui.DOM, the other just "DOM".
        but "DOM" is actually local, hence the "var" prefix.

        for PyV8, this might end up causing problems - we'll have
        to see: gen_mod_import and mod_var_name_decl might have
        to end up in a library-specific module, somewhere.
    """
    name = raw_module_name.split('.')
    if len(name) == 1:
        return ''
    child_name = name[-1]
    return 'var %s = %s;\n' % (child_name, raw_module_name)


def yn_bool(s):
    """Convert a yes/no string to a boolean.
       Strings beginning with 'Y' and 'y' return True, with 'N' and 'n' return False."""
    b = s and s.strip().upper()[0]
    if b == 'Y':
        return True
    elif b == 'N':
        return False
    else:
        raise ValueError("Expected 'Y' or 'N': %s" % s)


def R0(h, k, L, A):
    """ Resistance between ambient and first node // applies to end node as well

	Parameters
	----------
	h : float or (m,) np.array
		covection resistance [W/(m2K)]

	k : float or (m,) np.array
		material thermal conductivity [W/(mK)]

	L : float or (m,) np.array
		thickness of the sample [m]

	A : float or (m,) np.array
		sample area perpendicular to the direction of heat transfer [m2]

	Returns
	-------
	R : float or (m,) np.array
		heat transfer resistance [K/W]

	Example use
	-----------
	>>> XXX
	"""
    return L / (2 * k * A) + 1 / (h * A)


def KK_RC12(w, Rs, R_values, t_values):
    """
    Kramers-Kronig Function: -RC-

    Kristian B. Knudsen (kknu@berkeley.edu / kristianbknudsen@gmail.com)
    """
    return Rs + R_values[0] / (1 + w * 1.0j * t_values[0]) + R_values[1] / (
        1 + w * 1.0j * t_values[1]) + R_values[2] / (1 + w * 1.0j * t_values[2]
        ) + R_values[3] / (1 + w * 1.0j * t_values[3]) + R_values[4] / (1 +
        w * 1.0j * t_values[4]) + R_values[5] / (1 + w * 1.0j * t_values[5]
        ) + R_values[6] / (1 + w * 1.0j * t_values[6]) + R_values[7] / (1 +
        w * 1.0j * t_values[7]) + R_values[8] / (1 + w * 1.0j * t_values[8]
        ) + R_values[9] / (1 + w * 1.0j * t_values[9]) + R_values[10] / (1 +
        w * 1.0j * t_values[10]) + R_values[11] / (1 + w * 1.0j * t_values[11])


def sysmeta_prefix(resource):
    """
    Returns the system metadata prefix for given resource type.
    """
    if resource.lower() == 'object':
        return 'x-object-sysmeta-s3api-'
    else:
        return 'x-container-sysmeta-s3api-'


def ident(peak1, peak2):
    """
    Checks whether two peaks are the same peak.

    Parameters
    ----------
    peak1 : peak
    peak2 : peak

    Returns
    -------
    bool
        True if the peaks are the same peak, False otherwise.
    """
    return peak1['x'] == peak2['x'] and peak1['y'] == peak2['y']


@classmethod
def from_hops(cls, route_hops):
    """Build a route string from a list of route hops.

        :param route_hops: route hops
        :type route_hops: ``list`` of :class:`SAPRouterRouteHop`

        :return: route string
        :rtype: C{string}
        """
    result = ''
    for route_hop in route_hops:
        result += '/H/{}'.format(route_hop.hostname)
        if route_hop.port:
            result += '/S/{}'.format(route_hop.port)
        if route_hop.password:
            result += '/W/{}'.format(route_hop.password)
    return result


def classname(obj):
    """Given any object, returns the fully-qualified class name as a string"""
    module_str = obj.__class__.__module__
    class_str = obj.__class__.__name__
    return '{}.{}'.format(module_str, class_str)


def set_end_datetime(value):
    """Sets the end datetime to value (DateTime)"""


def write_recurrent_layer(model_name='sas', layer_name='recurrent',
    activation='tanh', src_layer='none', rnn_type='rnn', seq_output=
    'samelength', direction='forward', rnn_size=1, dropout=0.0):
    """
    Generate Python code defining a SAS deep learning recurrent layer

    Parameters
    ----------
    model_name : string, optional
        Name for deep learning model
    layer_name : string, optional
        Layer name
    activation : string, optional
        activation function
    src_layer : string, optional
        source layer(s) for the concat layer
    rnn_type : string, optional
        one of 'rnn', 'lstm', or 'gru'
    seq_output : string, optional
        one of 'samelength' or 'encoding'
    direction : boolean, optional
        indicates whether sequence processing
        performed in forward or reverse direction
    rnn_size : integer
        size of hidden dimension
    dropout : float, optional
        dropout rate, values range from 0.0 to 1.0

    Returns
    -------
    string

    """
    out = ['   s.addLayer(model=' + repr(model_name) + ', name=' + repr(
        layer_name) + ',', '              layer=dict(type="recurrent", n=' +
        str(rnn_size) + ',', '                         rnnType="' +
        rnn_type + '", act=' + repr(activation) + ', dropout=' + str(
        dropout) + ',', '                         outputType = "' +
        seq_output + '", reversed=' + repr(direction) + '),',
        '              srcLayers=' + src_layer + ')']
    return '\n'.join(out)


def norm_inf(x):
    """inf-norm: max(|x_i|)"""
    return max(abs(x))


def url(request):
    """called from the template for the download page."""


@classmethod
def add_organization(cls, options=None):
    """Associate an organization"""
    cls.command_sub = 'add-organization'
    return cls.execute(cls._construct_command(options))


@classmethod
def parse_args(cls, subparsers):
    """Add Softlayer ObjectStorage arguments to command line parser."""
    sl_oo_parser = subparsers.add_parser('softlayer', description=cls.__doc__)
    sl_oo_parser.add_argument('-u', '--username', help=
        'username for Softlayer ObjectStorage API')
    sl_oo_parser.add_argument('-p', '--api-key', help=
        'api key for Softlayer ObjectStorage API')
    sl_oo_parser.add_argument('-d', '--datacenter', help=
        'datacenter where the file will be stored')
    sl_oo_parser.add_argument('-c', '--container', help='target container')
    sl_oo_parser.add_argument('-n', '--network', default='private', help=
        "'public' or 'private' Softlayer network")
    sl_oo_parser.add_argument('local_path', nargs='?', default=None, help=
        'path in the local file system of the file to be uploaded')
    sl_oo_parser.add_argument('remote_path', help=
        """path on Softlayer ObjectStorage container where the file
                                  will be stored"""
        )


@classmethod
def merge(cls, edge1, edge2):
    """ Merges multi-color information from two supplied :class:`BGEdge` instances into a new :class:`BGEdge`

        Since :class:`BGEdge` represents an undirected edge, created edge's vertices are assign according to the order in first supplied edge.

        Accounts for subclassing.

        :param edge1: first out of two edge information from which is to be merged into a new one
        :param edge2: second out of two edge information from which is to be merged into a new one
        :return: a new undirected with multi-color information merged from two supplied :class:`BGEdge` objects
        :raises: ``ValueError``
        """
    if edge1.vertex1 != edge2.vertex1 and edge1.vertex1 != edge2.vertex2:
        raise ValueError('Edges to be merged do not connect same vertices')
    forward = edge1.vertex1 == edge2.vertex1
    if forward and edge1.vertex2 != edge2.vertex2:
        raise ValueError('Edges to be merged do not connect same vertices')
    elif not forward and edge1.vertex2 != edge2.vertex1:
        raise ValueError('Edges to be merged do not connect same vertices')
    return cls(vertex1=edge1.vertex1, vertex2=edge1.vertex2, multicolor=
        edge1.multicolor + edge2.multicolor)


@classmethod
def from_json_downstream(cls, json_data):
    """
        Initializes a value object from json_data that's been fetched for downstream data.

        :param json_data: JSON-data that initialize the object.
        :type json_data: dict
        """
    field_id = list(json_data.keys())[0]
    values = json_data[field_id]
    return cls(field_id=field_id, value=values.get('value'), timestamp=
        values.get('timestamp'))


def _improve_answer_span(doc_tokens, input_start, input_end, tokenizer,
    orig_answer_text):
    """Returns tokenized answer spans that better match the annotated answer."""
    tok_answer_text = ' '.join(tokenizer.tokenize(orig_answer_text))
    for new_start in range(input_start, input_end + 1):
        for new_end in range(input_end, new_start - 1, -1):
            text_span = ' '.join(doc_tokens[new_start:new_end + 1])
            if text_span == tok_answer_text:
                return new_start, new_end
    return input_start, input_end


def commandexecutor():
    """A context manager that resolves to an ipeercommandexecutor.

        The object this resolves to can be used to issue command requests
        to the peer.

        Callers should call its ``callcommand`` method to issue command
        requests.

        A new executor should be obtained for each distinct set of commands
        (possibly just a single command) that the consumer wants to execute
        as part of a single operation or round trip. This is because some
        peers are half-duplex and/or don't support persistent connections.
        e.g. in the case of HTTP peers, commands sent to an executor represent
        a single HTTP request. While some peers may support multiple command
        sends over the wire per executor, consumers need to code to the least
        capable peer. So it should be assumed that command executors buffer
        called commands until they are told to send them and that each
        command executor could result in a new connection or wire-level request
        being issued.
        """


def mesh_linked_tessfaces(mesh: 'bpy.types.Mesh') ->list:
    """Splits the mesh into connected faces, use this for seperating cubes from other mesh elements within 1 mesh datablock.

    :param mesh: the mesh used to group with.
    :type mesh: 'bpy.types.Mesh'
    :return:  lists of lists containing faces.
    """
    pass


@classmethod
def binder_add_page_details(cls, val):
    """
        Create an instance of this class set to the ``binder_add_page_details``
        tag with value ``val``.

        :param BinderAddPageDetails val:
        :rtype: EventDetails
        """
    return cls('binder_add_page_details', val)


def bookmark_add():
    """Add a bookmark for the selected/active directory

    """
    pass


def _convert_state(state):
    """Guess player state"""
    state = state.lower()
    if 'play' in state:
        return 'play'
    if 'pause' in state:
        return 'pause'
    if 'stop' in state:
        return 'stop'
    return 'fallback'


def last(n, foo):
    """Gets the last n items in foo."""
    if n == 0:
        return foo[len(foo):]
    else:
        return foo[-n:]


def parse_structure(astr, level):
    """
    The returned line number is from the beginning of the string, starting
    at zero. Returns an empty list if no loops found.

    """
    if level == 0:
        loopbeg = '/**begin repeat'
        loopend = '/**end repeat**/'
    else:
        loopbeg = '/**begin repeat%d' % level
        loopend = '/**end repeat%d**/' % level
    ind = 0
    line = 0
    spanlist = []
    while True:
        start = astr.find(loopbeg, ind)
        if start == -1:
            break
        start2 = astr.find('*/', start)
        start2 = astr.find('\n', start2)
        fini1 = astr.find(loopend, start2)
        fini2 = astr.find('\n', fini1)
        line += astr.count('\n', ind, start2 + 1)
        spanlist.append((start, start2 + 1, fini1, fini2 + 1, line))
        line += astr.count('\n', start2 + 1, fini2)
        ind = fini2
    spanlist.sort()
    return spanlist


def _append_attr_definition(definition, attr_name, attr_type):
    """ Adds an attribute definition if it is not already present.
    Dynamodb table creation requires to define attributes that are keys
    for the table or indexes. There must be no duplicated attribute
    definitions - aws throws ValidationException in such case.

    :type definition: []
    :type attr_name: str
    :type attr_type: str
    """
    for each in definition:
        if each['AttributeName'] == attr_name:
            return
    definition.append(dict(AttributeName=attr_name, AttributeType=attr_type))


def insert_ark(cnx, naan, name, qualifier=None):
    """Insert a record in "ark" table from specified values."""
    if qualifier is None:
        qs = 'INSERT INTO ark VALUES (%s, %s) RETURNING naan, name, qualifier;'
        values = naan, name
    else:
        qs = (
            'INSERT INTO ark VALUES (%s, %s, %s) RETURNING naan, name, qualifier;'
            )
        values = naan, name, qualifier
    (naan, name, qualifier), = cnx.system_sql(qs, values).fetchall()
    ark = u'/'.join([str(naan), name])
    if qualifier:
        ark += u'/' + qualifier
    return ark


def class_action_from_module_path(module_path):
    """Given the module name and path of a class, tries to retrieve the class.

    The loaded class can be used to instantiate new objects. """
    import importlib
    if '.' in module_path:
        module_name, _, class_name = module_path.rpartition('.')
        m = importlib.import_module(module_name)
        m = importlib.reload(m)
        return getattr(m, class_name)
    else:
        return globals()[module_path]


@property
def exponent(f):
    """
        Return the exponent as an integer.

        Convert the bits of the exponent part as if they are an
        unsigned integer:
        2^0 * bit_1 + 2^1 * bit_2 + 2^3 * bit_3 + ...
        """
    exponent = 0
    for bit, n in zip(f.exponent_bits, range(f.k)[::-1]):
        if bit:
            exponent += 2 ** n
    return exponent


def s3_isfile(filepath, bucket):
    """
    @doc: check a file for existence in an S3 bucket
    @args: filepath is a str
           bucket is a boto3.resource.Bucket object
    @return: boolean whether or not the file exists
    """
    if bucket is None:
        return False
    objs = list(bucket.objects.filter(Prefix=filepath))
    return len(objs) > 0 and objs[0].key == filepath


def si_to_kmh(vals):
    """Conversion from SI wind speed units to km/hr.

    Note
    ----
    Code was migrated from https://github.com/nguy/PyRadarMet.

    Parameters
    ----------
    vals : float
        float or array of floats
        Speed in SI units (m/s)

    Returns
    -------
    output: float
        float or array of floats
        Speed in km/hr

    Examples
    --------
    >>> from wradlib.trafo import si_to_kmh
    >>> print(si_to_kmh(1.))
    3.6
    """
    return vals * 3600.0 / 1000.0


def angstroms_to_s(offset_us, source_to_detector_m, array):
    """convert array in angstroms into s

    Parameters:
    ===========
    numpy array of lambda in angstroms
    offset_us: float. Delay of detector in us
    source_to_detector_m: float. Distance source to detector in m

    Returns:
    ========
    numpy array of time in s
    """
    return source_to_detector_m * array / 3956.0 - offset_us * 1e-06


def error_msg_from_exception(e):
    """Translate exception into error message

    Database have different ways to handle exception. This function attempts
    to make sense of the exception object and construct a human readable
    sentence.

    TODO(bkyryliuk): parse the Presto error message from the connection
                     created via create_engine.
    engine = create_engine('presto://localhost:3506/silver') -
      gives an e.message as the str(dict)
    presto.connect('localhost', port=3506, catalog='silver') - as a dict.
    The latter version is parsed correctly by this function.
    """
    msg = ''
    if hasattr(e, 'message'):
        if isinstance(e.message, dict):
            msg = e.message.get('message')
        elif e.message:
            msg = e.message
    return msg or str(e)


def islower(text):
    """
    Checks if  all cased characters in ``text`` are lowercase and there is at least one cased character.

    Cased characters are defined by the Unicode standard.  All alphabetic characters in the
    ASCII character set are cased.

    :param text: The string to check
    :type text:  ``str``

    :return: True if all cased characters in ``text`` are lowercase and there is at least one cased character, False otherwise.
    :rtype:  ``bool``
    """
    assert isinstance(text, str), '%s is not a string' % text
    return text.islower()


def split_ring(grid):
    """ Reinforce MV grid by splitting a critical ring into two new rings according to dena

    Parameters
    ----------
        grid : :class:`~.ding0.core.network.grids.MVGridDing0`
            Grid identifier.

    Returns
    -------
    type
        #TODO: Description of return. Change type in the previous line accordingly
    """
    pass


def setMatchScore(match_score):
    """Set match score used to align sequences."""
    if isinstance(match_score, (float, int)) and match_score >= 0:
        global MATCH_SCORE
        MATCH_SCORE = match_score
    else:
        raise TypeError('match_score must be a positive number or zero')


def read_map_pixel(map_name: str, level: int, x: int, y: int) ->float:
    """Reads a pixel in a user-defined map.

    :param map_name: The name of the map.
    :type map_name: str
    :param level: The level of the pyramid in which we wish to read the pixel.
    :type level: int
    :param x: The x coordinate of the pixel we wish to read. The origin is in the lower-left corner.
    :type x: int
    :param y: The y coordinate of the pixel we wish to read. The origin is in the lower-left corner.
    :type y: int
    :return:  The floating-point value stored for that pixel.
    """
    pass


def split_args(args):
    """
    Split a list of argument strings into a dictionary where each key is an
    argument name.

    An argument looks like ``crop``, ``crop="some option"`` or ``crop=my_var``.
    Arguments which provide no value get a value of ``True``.
    """
    args_dict = {}
    for arg in args:
        split_arg = arg.split('=', 1)
        if len(split_arg) > 1:
            value = split_arg[1]
        else:
            value = True
        args_dict[split_arg[0]] = value
    return args_dict


def isiterable(coll):
    """Return True if the collection is any iterable except a string

    :param coll: a collection
    :returns: a boolean

    >>> isiterable(1)
    False
    >>> isiterable('iterable')
    False
    >>> isiterable([1, 2, 3])
    True

    """
    return hasattr(coll, '__iter__')


def sort_arg(string):
    """converter appropriate for command line argument conversion"""
    forward = True
    if string.startswith('-'):
        forward = False
        string = string[1:]
    return int(string), forward


def aggregated_tex(f, entropy_matrix):
    """
    Write in latex file entropy of 6 groups (need to be change to adapt number of groups)

    Parameters
    ----------
        f: file

        entropy_matrix: numpy array from proportion_group analysis

    Returns
    -------
        File (Optionnal)
    """
    f.write('\n% 4. Aggregated Diversity')
    f.write('\n\\newcommand{\\%s}{%.2f}' % ('EntropyGroupA', entropy_matrix[0])
        )
    f.write('\n\\newcommand{\\%s}{%.2f}' % ('EntropyGroupB', entropy_matrix[1])
        )
    f.write('\n\\newcommand{\\%s}{%.2f}' % ('EntropyGroupC', entropy_matrix[2])
        )
    f.write('\n\\newcommand{\\%s}{%.2f}' % ('EntropyGroupD', entropy_matrix[3])
        )
    f.write('\n\\newcommand{\\%s}{%.2f}' % ('EntropyGroupE', entropy_matrix[4])
        )
    f.write('\n\\newcommand{\\%s}{%.2f}' % ('EntropyGroupF', entropy_matrix[5])
        )
    return f


def psi_Na_CO3_HSO4_HMW84(T, P):
    """c-a-a': sodium carbonate bisulfate [HMW84]."""
    psi = 0.0
    valid = T == 298.15
    return psi, valid


@classmethod
def get_help(cls):
    """ Returns the description of the type of Resource

        """
    return cls._help


def translate(cls, key):
    """Get the translated message for a key."""
    return cls.instance.getmessage(key)


def scroll_one_line_up(event):
    """
    scroll_offset -= 1
    """
    w = event.app.layout.current_window
    b = event.app.current_buffer
    if w:
        if w.render_info:
            info = w.render_info
            if w.vertical_scroll > 0:
                first_line_height = info.get_height_for_line(info.
                    first_visible_line())
                cursor_up = info.cursor_position.y - (info.window_height -
                    1 - first_line_height - info.configured_scroll_offsets.
                    bottom)
                for _ in range(max(0, cursor_up)):
                    b.cursor_position += b.document.get_cursor_up_position()
                w.vertical_scroll -= 1


def get_python_version():
    """
    A convenience function to get the python version used to run this
    tutorial. This ensures that the conda environment is created with an
    available version of python.

    """
    import platform
    versions = {'2': '2.7.15', '3': '3.7.3'}
    return versions[platform.python_version_tuple()[0]]


def check_wanted(zelf, *args, **kwargs):
    """ whether an object is desired. """
    want = args[0]
    for key, value in want.items():
        if key == 'format':
            continue
        if key not in zelf:
            continue
        if value.startswith('-'):
            continue
        if value not in str(zelf[key]):
            return False
    return True


def get_project_name(project_id, projects):
    """Retrieves project name for given project id

    Args:
        projects: List of projects
        project_id: project id

    Returns: Project name or None if there is no match
    """
    for project in projects:
        if project_id == project.id:
            return project.name


def hide_socket_toggle():
    """Toggle unused node socket display

    """
    pass


def scan_setup_py():
    """Validate the contents of setup.py against Versioneer's expectations."""
    found = set()
    setters = False
    errors = 0
    with open('setup.py', 'r') as f:
        for line in f.readlines():
            if 'import versioneer' in line:
                found.add('import')
            if 'versioneer.get_cmdclass()' in line:
                found.add('cmdclass')
            if 'versioneer.get_version()' in line:
                found.add('get_version')
            if 'versioneer.VCS' in line:
                setters = True
            if 'versioneer.versionfile_source' in line:
                setters = True
    if len(found) != 3:
        print('')
        print('Your setup.py appears to be missing some important items')
        print('(but I might be wrong). Please make sure it has something')
        print('roughly like the following:')
        print('')
        print(' import versioneer')
        print(' setup( version=versioneer.get_version(),')
        print('        cmdclass=versioneer.get_cmdclass(),  ...)')
        print('')
        errors += 1
    if setters:
        print("You should remove lines like 'versioneer.VCS = ' and")
        print("'versioneer.versionfile_source = ' . This configuration")
        print('now lives in setup.cfg, and should be removed from setup.py')
        print('')
        errors += 1
    return errors


def convert_y_domain(mpl_plot_bounds, mpl_max_y_bounds):
    """Map y dimension of current plot to plotly's domain space.

    The bbox used to locate an axes object in mpl differs from the
    method used to locate axes in plotly. The mpl version locates each
    axes in the figure so that axes in a single-plot figure might have
    the bounds, [0.125, 0.125, 0.775, 0.775] (x0, y0, width, height),
    in mpl's figure coordinates. However, the axes all share one space in
    plotly such that the domain will always be [0, 0, 1, 1]
    (x0, y0, x1, y1). To convert between the two, the mpl figure bounds
    need to be mapped to a [0, 1] domain for x and y. The margins set
    upon opening a new figure will appropriately match the mpl margins.

    Optionally, setting margins=0 and simply copying the domains from
    mpl to plotly would place axes appropriately. However,
    this would throw off axis and title labeling.

    Positional arguments:
    mpl_plot_bounds -- the (x0, y0, width, height) params for current ax **
    mpl_max_y_bounds -- overall (y0, y1) bounds for all axes **

    ** these are all specified in mpl figure coordinates

    """
    mpl_y_dom = [mpl_plot_bounds[1], mpl_plot_bounds[1] + mpl_plot_bounds[3]]
    plotting_height = mpl_max_y_bounds[1] - mpl_max_y_bounds[0]
    y0 = (mpl_y_dom[0] - mpl_max_y_bounds[0]) / plotting_height
    y1 = (mpl_y_dom[1] - mpl_max_y_bounds[0]) / plotting_height
    return [y0, y1]


def google_list_style(style):
    """finds out whether this is an ordered or unordered list"""
    if 'list-style-type' in style:
        list_style = style['list-style-type']
        if list_style in ['disc', 'circle', 'square', 'none']:
            return 'ul'
    return 'ol'


def nest(f, expr, n):
    """
    returns the value of f applied to expr n times

    Parameters
    ----------
    f : function
        function to be applied.
    expr : any
        expression f is to be applied to.
    n : int
        number of times.

    Returns
    -------
    expr : any
        result of f(f(f(....expr ...))) nested n deep

    Examples
    --------
    >>> xt.nest(lambda x: x/10, 123456, 4)
    12.3456

    >>> xt.nest(xt.first, [['a','b'],['c','d']], 2)
    'a'
    """
    for i in range(n):
        expr = f(expr)
    return expr


def get_topic_attributes(TopicArn=None):
    """
    Returns all of the properties of a topic. Topic properties returned might differ based on the authorization of the user.
    See also: AWS API Documentation

    :example: response = client.get_topic_attributes(
        TopicArn='string'
    )

    :type TopicArn: string
    :param TopicArn: [REQUIRED]
            The ARN of the topic whose properties you want to get.

    :rtype: dict
    :return: {
        'Attributes': {
            'string': 'string'
        }
    }

    :returns:
    (string) --
    (string) --

    """
    pass


@classmethod
def callback_hash_tag_name_consitency(cls, element, hashpipe, children=True):
    """
        calculate hash over tag

        :param element: etree.element
        :param hashpipe: hash algorithm interface
        :param children: bool - if false children of element ignored for hash calculation
        """
    _element_childes = element.getchildren()
    if children:
        for child in _element_childes:
            hashpipe.update(cls.callback_hash_tag_name_consitency(child,
                hashpipe))
    hashpipe.update(bytes(str(element.tag) + '#tag', 'utf-8'))
    return bytes(hashpipe.hexdigest(), 'utf-8')


def add_tags(pipelineId=None, tags=None):
    """
    Adds or modifies tags for the specified pipeline.
    See also: AWS API Documentation

    :example: response = client.add_tags(
        pipelineId='string',
        tags=[
            {
                'key': 'string',
                'value': 'string'
            },
        ]
    )

    :type pipelineId: string
    :param pipelineId: [REQUIRED]
            The ID of the pipeline.

    :type tags: list
    :param tags: [REQUIRED]
            The tags to add, as key/value pairs.
            (dict) --Tags are key/value pairs defined by a user and associated with a pipeline to control access. AWS Data Pipeline allows you to associate ten tags per pipeline. For more information, see Controlling User Access to Pipelines in the AWS Data Pipeline Developer Guide .
            key (string) -- [REQUIRED]The key name of a tag defined by a user. For more information, see Controlling User Access to Pipelines in the AWS Data Pipeline Developer Guide .
            value (string) -- [REQUIRED]The optional value portion of a tag defined by a user. For more information, see Controlling User Access to Pipelines in the AWS Data Pipeline Developer Guide .

    :rtype: dict
    :return: {}

    """
    pass


def add_edge_end_unused(intersection, duplicates, intersections):
    """Add intersection that is ``COINCIDENT_UNUSED`` but on an edge end.

    This is a helper for :func:`~._py_triangle_intersection.add_intersection`.
    It assumes that

    * ``intersection`` will have at least one of ``s == 0.0`` or ``t == 0.0``
    * A "misclassified" intersection in ``intersections`` that matches
      ``intersection`` will be the "same" if it matches both ``index_first``
      and ``index_second`` and if it matches the start index exactly

    Args:
        intersection (.Intersection): An intersection to be added.
        duplicates (List[.Intersection]): List of duplicate intersections.
        intersections (List[.Intersection]): List of "accepted" (i.e.
            non-duplicate) intersections.
    """
    found = None
    for other in intersections:
        if (intersection.index_first == other.index_first and intersection.
            index_second == other.index_second):
            if intersection.s == 0.0 and other.s == 0.0:
                found = other
                break
            if intersection.t == 0.0 and other.t == 0.0:
                found = other
                break
    if found is not None:
        intersections.remove(found)
        duplicates.append(found)
    intersections.append(intersection)


def snake_to_darwin_case(text: str) ->str:
    """Convert snake_case to Darwin_Case."""
    return '_'.join(map(str.capitalize, text.split('_')))


def convert(name: str) ->str:
    """Convert path name into valid JSON.

    Parameters
    ----------
    name : str
        a path name

    Returns
    -------
    str
        valid JSON path

    """
    if '-' in name or ' ' in name:
        return '["{}"]'.format(name[1:])
    return name


@classmethod
def directory_restrictions_remove_members(cls, val):
    """
        Create an instance of this class set to the
        ``directory_restrictions_remove_members`` tag with value ``val``.

        :param DirectoryRestrictionsRemoveMembersType val:
        :rtype: EventType
        """
    return cls('directory_restrictions_remove_members', val)


def key_from_image_name(image_name):
    """
    >>> key_from_image_name('shipwright/blah:1234')
    '1234'
    """
    return image_name.split(':', 1)[1]


def p_varargslist_9(p):
    """varargslist : fpdef varargslist_star"""
    p[2].args.insert(0, p[1])
    p[0] = p[2]


def _macro_defn_action(_s, _l, tokens):
    """
    Builds a dictionary structure which defines the given macro.
    """
    assert len(tokens) == 3
    assert tokens[0] == '@'
    return {tokens[1]: tokens[2]}


def _add_extract_args(parser):
    """
    mideu add extract subcommand arguments

    :param parser: the argparse parser
    :return: None
    """
    csv_arg_group = parser.add_argument_group('csv output options')
    csv_arg_group.add_argument('--csvoutputfile', help='Output filename')


def principal_permission_grant(principal, permission):
    """Return the principal-permission grant if any

        The return value is one of Allow, Deny, or Unset
        """


def conv_to_number(val, conv):
    """Convert a string to a number

    Can apply either a k (thousand) or M (million) multiplier

    Parameter
    ---------
    val : str
      String representing the number
    conv
      Function to apply to final number, float/int

    Example
    -------
    ('100k', int) -> 100,000
    ('5.6M', float) -> 5,600,000.0

    """
    if val[-1].isalpha():
        val, suffix = val[:-1], val[-1]
        try:
            multi = {'k': 1000.0, 'M': 1000000.0}[suffix]
        except KeyError:
            raise ValueError('Unrecognised suffix {}'.format(suffix))
    else:
        multi = 1
    return conv(float(val) * multi)


def order_sort(item):
    """Attempt to sort modules by order keys.

    Always returns an integer for compatibility.

    Args:
        item (dict): The module to sort

    Returns:
        int: The sort order integer, or -1 if the item cannot be sorted.
    """
    if item is None or item.get('order') is None:
        return -1
    try:
        return int(item['order'])
    except (ValueError, TypeError):
        return -1


def _nova_error_message(msg_type, message, status_code, request):
    """
    Set the response code on the request, and return a JSON blob representing
    a Nova error body, in the format Nova returns error messages.

    :param str msg_type: What type of error this is - something like
        "badRequest" or "itemNotFound" for Nova.
    :param str message: The message to include in the body.
    :param int status_code: The status code to set
    :param request: the request to set the status code on

    :return: dictionary representing the error body
    """
    request.setResponseCode(status_code)
    return {msg_type: {'message': message, 'code': status_code}}


@classmethod
def redraw(cls):
    """
        Redraws the window.
        """
    cls.erase()
    if cls.BORDERED:
        cls.WINDOW.border()
    if cls.MENU:
        cls._cw_menu_display()
    cls.refresh()


def delete(requestor, service, nodeIdentifier):
    """
        Called when a node deletion request has been received.

        @param requestor: The entity the request originated from.
        @type requestor: L{JID<twisted.words.protocols.jabber.jid.JID>}
        @param service: The entity the request was addressed to.
        @type service: L{JID<twisted.words.protocols.jabber.jid.JID>}
        @param nodeIdentifier: The identifier of the node to be delete.
        @type nodeIdentifier: C{unicode}
        """


def trans(ele, standard=False):
    """Translates esprima syntax tree to python by delegating to appropriate translating node"""
    try:
        node = globals().get(ele['type'])
        if not node:
            raise NotImplementedError('%s is not supported!' % ele['type'])
        if standard:
            node = node.__dict__['standard'
                ] if 'standard' in node.__dict__ else node
        return node(**ele)
    except:
        raise


@staticmethod
def get_opt(opt_name, args):
    """Return option name and value.

        :param opt_name: name of the option, e.g., "username"
        :param args: parsed arguments
        """
    return opt_name, getattr(args, 'os_%s' % opt_name, None)


def get_via_cycle(job_state_map, job):
    """
    Find a possible cycle including via_job.

    :param job_state_map:
        A dictionary mapping job.id to a JobState object.
    :param via_job:
        Any job, start of a hypothetical via job cycle.
    :raises KeyError:
        If any of the encountered jobs are not present in job_state_map.
    :return:
        A list of jobs that represent the cycle or an empty tuple if no cycle
        is present. The list has the property that item[0] is item[-1]

    A via cycle occurs if *job* is reachable through the *via_job* by
    recursively following via_job connection until via_job becomes None.
    """
    cycle = []
    seen = set()
    while job is not None:
        cycle.append(job)
        seen.add(job)
        next_job = job_state_map[job.id].via_job
        if next_job in seen:
            break
        job = next_job
    else:
        return ()
    cycle.append(next_job)
    return cycle


def create_axisdic(adic, tlen, dlen):
    """
    Make an Sparky axis dictionary from a universal axis dictionary.

    Parameters
    ----------
    adic : dict
        Axis dictionary from a universal dictionary.
    tlen : int
        Tile length of axis.
    dlen : int
        Data length of axis.

    Returns
    -------
    sdic : dict
        Sparky axis dictionary

    """
    dic = dict()
    dic['nucleus'] = adic['label']
    dic['spectral_shift'] = 0
    dic['npoints'] = int(dlen)
    dic['size'] = int(dlen)
    dic['bsize'] = int(tlen)
    dic['spectrometer_freq'] = float(adic['obs'])
    dic['spectral_width'] = float(adic['sw'])
    dic['xmtr_freq'] = float(adic['car']) / dic['spectrometer_freq']
    dic['zero_order'] = 0.0
    dic['first_order'] = 0.0
    dic['first_pt_scale'] = 0.0
    dic['extended'] = b'\x80'
    return dic


@classmethod
def quote_web_sales(cls, sales):
    """
        Quote the sale, process the payments
        """
    cls.set_number(sales)
    cls.quote(sales)
    for sale in sales:
        sale.process_pending_payments()
        sale.settle_manual_payments()
        sale.handle_payment_on_confirm()