def load_schema(filename):
    """先加载schema.sql文件来获取所有建表语句"""
    result = []
    with open(filename, encoding='utf-8') as sqlfile:
        each_table = []
        for line in sqlfile:
            if not line.strip() or line.strip().startswith('#'):
                continue
            line = line.replace('`', '')
            if line.startswith('--'):
                temp_comment = line.split('--')[1].strip()
            elif 'DROP TABLE' in line:
                each_table.insert(0, temp_comment)
                each_table.insert(1, line.strip().split()[-1][:-1])
            elif ' COMMENT ' in line and 'ENGINE=' not in line:
                col_arr = line.split()
                col_name = col_arr[0]
                col_type = col_arr[1]
                if 'PRIMARY KEY' in line or 'NOT NULL' in line:
                    col_null = 'NOT NULL'
                else:
                    col_null = ''
                col_remark = line.split(' COMMENT ')
                cr = col_remark[-1].strip().replace("'", '')
                each_table.append((col_name, col_type, col_null, cr[:-1] if
                    cr.endswith(',') else cr))
            elif 'ENGINE=' in line:
                result.append(list(each_table))
                each_table.clear()
    return result


@classmethod
def webSettings(cls):
    """
        Class method to get the web settings of the current profile.

        @return web settings of the current profile
        @rtype QWebEngineSettings
        """
    return cls.webProfile().settings()


def master_template_filename(plan_name):
    """Generates the filename of a deployment plan's master template when
    it is written out as a Heat template.

    :type plan_name: str
    :rtype: str
    """
    return '%s-%s' % (plan_name, 'template.yaml')


def openDB(filename, *args):
    """Open a database with given *filename*."""
    try:
        import anydbm as dbm
    except ImportError:
        import dbm
    return dbm.open(filename, *args)


def is_valid_field_name(name):
    """
    Check that field name is valid:
    * can't start or end with whitespace characters
    * can't start with underscore
    * can't contain double quote (")
    * can't be empty
    """
    return name and name == name.strip() and not name.startswith('_'
        ) and '"' not in name


@staticmethod
def _limit_timeout_value(timeout):
    """
        This prevents from overflowing C long int
        :param timeout: timeout value
        :type timeout: int

        :return: timeout value that fits C long int
        :rtype: int
        """
    return timeout if timeout <= 2 ** 32 else 2 ** 32


def _watch_callback(obj_weakref, method_name, *args, **kwargs):
    """
    A callback handler for the ControlConnection that tolerates
    weak references.
    """
    obj = obj_weakref()
    if obj is None:
        return
    getattr(obj, method_name)(*args, **kwargs)


def get_zip_file_dir(locator):
    """
    Returns the relative directory path where we are saving the zipped submissions file.

    Args:
        locator (BlockUsageLocator): BlockUsageLocator for the sga module
    """
    return '{loc.org}/{loc.course}/{loc.block_type}_zipped'.format(loc=locator)


@staticmethod
def p_ecu_address(p):
    """ecu_address : ECU_ADDRESS NUMERIC"""
    p[0] = p[2]


@staticmethod
def odinweb_node_formatter(path_node):
    """
        Format a node to be consumable by the `UrlPath.parse`.
        """
    args = [path_node.name]
    if path_node.type:
        args.append(path_node.type.name)
    if path_node.type_args:
        args.append(path_node.type_args)
    return '{{{}}}'.format(':'.join(args))


def cond(ctx, A, norm=None):
    """
        Calculate the condition number of a matrix using a specified matrix norm.

        The condition number estimates the sensitivity of a matrix to errors.
        Example: small input errors for ill-conditioned coefficient matrices
        alter the solution of the system dramatically.

        For ill-conditioned matrices it's recommended to use qr_solve() instead
        of lu_solve(). This does not help with input errors however, it just avoids
        to add additional errors.

        Definition:    cond(A) = ||A|| * ||A**-1||
        """
    if norm is None:
        norm = lambda x: ctx.mnorm(x, 1)
    return norm(A) * norm(ctx.inverse(A))


def exists(env):
    """Standard library tar function should always exist."""
    return True


def COUNT_DISTINCT(src_column):
    """
    Builtin unique counter for groupby. Counts the number of unique values

    Example: Get the number of unique ratings produced by each user.

    >>> sf.groupby("user",
    ...    {'rating_distinct_count':tc.aggregate.COUNT_DISTINCT('rating')})
    """
    return '__builtin__count__distinct__', [src_column]


@classmethod
def getBytes(cls, value):
    """
        Convert string to byte array.
        value: String value.
        returns String as bytes.
        """
    if not value:
        return None
    return value.encode()


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

        :param SharedFolderChangeMembersInheritancePolicyType val:
        :rtype: EventType
        """
    return cls('shared_folder_change_members_inheritance_policy', val)


@classmethod
def find_or_create_for_party_using_ps_data(cls, party, address_record):
    """Look for the address in tryton corresponding to the address_record.
        If found, return the same else create a new one and return that.

        :param address_record: Objectified XML record sent by pystashop
        :param party: Active Record of Party
        :returns: Active record of created address
        """
    for address in party.addresses:
        if address.match_with_ps_data(address_record):
            break
    else:
        address = cls.create_for_party_using_ps_data(party, address_record)
    return address


def isunauthenticated(func):
    """Checks if the function does not require authentication.

    Mark such functions with the `@unauthenticated` decorator.

    :returns: bool
    """
    return getattr(func, 'unauthenticated', False)


def smooth(signal, owidth, edge_truncate=False):
    """Replicates the IDL ``SMOOTH()`` function.

    Parameters
    ----------
    signal : array-like
        The array to be smoothed.
    owidth : :class:`int` or array-like
        Width of the smoothing window.  Can be a scalar or an array with
        length equal to the number of dimensions of `signal`.
    edge_truncate : :class:`bool`, optional
        Set `edge_truncate` to ``True`` to apply smoothing to all points.
        Points near the edge are normally excluded from smoothing.

    Returns
    -------
    array-like
        A smoothed array with the same dimesions and type as `signal`.

    References
    ----------
    http://www.harrisgeospatial.com/docs/smooth.html

    Examples
    --------
    """
    if owidth % 2 == 0:
        width = owidth + 1
    else:
        width = owidth
    if width < 3:
        return signal
    n = signal.size
    istart = int((width - 1) / 2)
    iend = n - int((width + 1) / 2)
    w2 = int(width / 2)
    s = signal.copy()
    for i in range(n):
        if i < istart:
            if edge_truncate:
                s[i] = (signal[0:istart + i + 1].sum() + (istart - i) *
                    signal[0]) / float(width)
        elif i > iend:
            if edge_truncate:
                s[i] = (signal[i - istart:n].sum() + (i - iend) * signal[n - 1]
                    ) / float(width)
        else:
            s[i] = signal[i - w2:i + w2 + 1].sum() / float(width)
    return s


@staticmethod
def unset_update(keys):
    """
        Creates a delete query to remove certain keys from a document or list of documents.
        :param keys: a list of keys to delete (or a single key)
        :return: a dictionary containing a proper/valid update statement for MongoDB.
        """
    unset_update = {'$unset': {}}
    if isinstance(keys, str):
        unset_update[keys] = 1
    elif isinstance(keys, list):
        for key in keys:
            unset_update[key] = 1
    return unset_update


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


def get_prefix_path(path, delimiter='/'):
    """get dirname-equivalent path prefix for a given delimiter"""
    if path == delimiter:
        return path
    split_path = path.rstrip(delimiter).split(delimiter)
    if len(split_path) <= 2:
        return delimiter
    else:
        return delimiter.join(split_path[:-1])


def set_testmode(testing):
    """Sets the package testing mode.
    """
    global testmode
    testmode = testing


def asbool(s):
    """Convert value to bool. Copied from pyramid.settings."""
    if s is None:
        return False
    if isinstance(s, bool):
        return s
    s = str(s).strip()
    return s.lower() in ('t', 'true', 'y', 'yes', 'on', '1')


def construct_search(field_name):
    """
    Вспомогательная функция для построения поисковых полей
    """
    if field_name.startswith('^'):
        return '%s__istartswith' % field_name[1:]
    elif field_name.startswith('='):
        return '%s__iexact' % field_name[1:]
    elif field_name.startswith('@'):
        return '%s__search' % field_name[1:]
    return '%s__icontains' % field_name


def nice_frequency(frequency):
    """return a string of the frequency with SI unit and a reasonable number
       of digits"""
    if frequency < 1000.0:
        return '%dHz' % frequency
    elif frequency < 10000.0:
        return '%.3fkHz' % (frequency / 1000.0)
    elif frequency < 100000.0:
        return '%.2fkHz' % (frequency / 1000.0)
    elif frequency < 1000000.0:
        return '%.1fkHz' % (frequency / 1000.0)
    elif frequency < 10000000.0:
        return '%.3fMHz' % (frequency / 1000000.0)
    elif frequency < 1000000000.0:
        return '%.2fMHz' % (frequency / 1000000.0)
    return '%.2fGHz' % (frequency / 1000000000.0)


def max_util_of_info(c, A, b, bounds, y):
    """
    Compute the maximum utility of information at scale `y`.

    Parameters
    ----------
    c : ndarray
        A list of atom-weights.
    A : ndarray
        The lhs of the various constraints.
    b : ndarray
        The rhs of the various constraints.
    bounds : list of pairs
        Each part of `x` must be between the atom's value and 0.
    y : float
        The total mutual information captured.
    """
    from scipy.optimize import linprog
    b[-1] = y
    solution = linprog(c, A, b, bounds=bounds)
    maximum_utility_of_information = -solution.fun
    return maximum_utility_of_information


@classmethod
def keyforenum(cls, enum):
    """ name of key for enum"""
    if enum < len(cls.keys):
        return cls.keys[enum]


@staticmethod
def url_to_tile(url):
    """ Extracts tile name, date and AWS index from tile url on AWS.

        :param url: class input parameter 'metafiles'
        :type url: str
        :return: Name of tile, date and AWS index which uniquely identifies tile on AWS
        :rtype: (str, str, int)
        """
    info = url.strip('/').split('/')
    name = ''.join(info[-7:-4])
    date = '-'.join(info[-4:-1])
    return name, date, int(info[-1])


def formula_2013_01_01(foyer_fiscal, period, parameters):
    """
        Les dépenses de travaux dépendent d'un bouquet de travaux
        2013
        """
    f7sd = foyer_fiscal('f7sd', period)
    f7se = foyer_fiscal('f7se', period)
    f7sn = foyer_fiscal('f7sn', period)
    f7so = foyer_fiscal('f7so', period)
    f7sp = foyer_fiscal('f7sp', period)
    f7sq = foyer_fiscal('f7sq', period)
    f7sr = foyer_fiscal('f7sr', period)
    f7ss = foyer_fiscal('f7ss', period)
    f7st = foyer_fiscal('f7st', period)
    f7vg = foyer_fiscal('f7vg', period)
    f7wc = foyer_fiscal('f7wc', period)
    f7wh = foyer_fiscal('f7wh', period)
    f7wt = foyer_fiscal('f7wt', period)
    t1 = f7wt
    t2 = f7wc
    t3 = f7vg
    t4 = (f7sn + f7so > 0) * 1
    t5 = (f7sr + f7ss > 0) * 1
    t6 = (f7st + f7sp + f7sq + f7sd + f7se > 0) * 1
    bouquet = (t1 + t2 + t3 + t4 + t5 + t6 > 1) * (f7wh == 1)
    return bouquet


def ascii(value):
    """Return the string of value

    :param mixed value: The value to return
    :rtype: str

    """
    return '{0}'.format(value)


@classmethod
def delete(cls, handle):
    """ Delete a no-longer-used parse job """
    with cls._lock:
        del cls._jobs[handle]


def read_data_attributes_from(group, paths):
    """
    Reads simple (float, string, int, etc) leaves from
    a nested description of paths out of a NeXuS file.
    :param group:
    :param paths:
    :return:
    """
    read_attrs = {}
    original_group = group
    for path, attributes in paths:
        group = original_group
        for p in path:
            group = group[p]
        for attribute_name in attributes:
            try:
                data = group[attribute_name]['data']
            except ValueError:
                data = group[attribute_name]
            try:
                data = data[:]
            except ValueError:
                data = data.value
            read_attrs[attribute_name] = data
    return read_attrs


@staticmethod
def _get_type_SQL(access_type):
    """Access vartypes to SQL vartypes"""
    switcher = {'LONGBINARY': 'varbinary', 'BIT': 'bit', 'COUNTER': 'int',
        'CURRENCY': 'float', 'GUID': 'int', 'DOUBLE': 'double', 'INTEGER':
        'int', 'BYTE': 'int', 'SMALLINT': 'int', 'DECIMAL': 'float', 'REAL':
        'int', 'DATETIME': 'datetime', 'VARCHAR': 'varchar', 'LONGCHAR':
        'longtext'}
    return switcher.get(access_type, 'varchar')


def do_replace(text, params):
    """
    Ищет знаки '#' в тексте rtf-шаблона и подставляет значения из словаря
    """
    for key_param, value in params.items():
        if key_param in text:
            text = text.replace('#%s#' % key_param, value)
    return text


def megamanicHackRequest():
    """Alters the request to edit multiple objects and fields."""


@classmethod
def from_spec_list(cls, specs, validator_map, resource_roots, package='sahara'
    ):
    """Constructs a list of validators from a list of specifications.

        :param specs: A list of validator specifications, each of which
            will be a dict of size 1, where the key represents the validator
            type and the value respresents its specification.
        :param validator_map: A map of validator name to class.
        :param resource_roots: The roots from which relative paths to
            resources (scripts and such) will be referenced. Any resource will
            be pulled from the first path in the list at which a file exists.
        :return: A list of validators.
        """
    validators = []
    for spec in specs:
        validator_class, validator_spec = cls.get_class_from_spec(spec,
            validator_map)
        validators.append(validator_class.from_spec(validator_spec,
            validator_map, resource_roots, package))
    return validators


@classmethod
def query_by_names(cls, names):
    """Query group by a list of group names.

        :param list names: List of the group names.
        :returns: Query object.
        """
    assert isinstance(names, list)
    return cls.query.filter(cls.name.in_(names))


def append_case_duration(el, datetime_format='%Y/%m/%d %H:%M:%S.%f'):
    """Calculate and append case duration information to cases in a
    given event log.

    Parameters
    ----------
    el : DataFrame
        An event log.
    datetime_format : str, optional, default '%Y/%m/%d %H:%M:%S.%f'
        The format string for parsing the timestamps in the given event
        log. Defaults to ``'%Y/%m/%d %H:%M:%S.%f'``, e.g. 2019/12/6
        10:48:51.0. See `Python strftime() and strptime() Behavior` for
        more information.

    Returns
    el : DataFrame
        An event log.
    """
    from datetime import datetime
    l_case_duration = [float('nan')] * len(el)
    for case_id, trace in el.groupby('case_id'):
        start_time = datetime.strptime(trace.iloc[0]['timestamp'],
            datetime_format)
        complete_time = datetime.strptime(trace.iloc[-1]['timestamp'],
            datetime_format)
        duration_seconds = (complete_time - start_time).total_seconds()
        for event_index in trace.index:
            l_case_duration[event_index] = duration_seconds
    el['case_duration'] = l_case_duration
    return el


def day_of_week(rdate):
    """Get the ordinal day of the week.

    Parameters
    ----------
    rdate : int
        RDate date format.

    Returns
    -------
    dw : int
        Ordinal day of the week.
    """
    dw = rdate % 7
    return dw


@staticmethod
def types():
    """
        Return static column types
        """
    return ['varchar(25)'] + ['decimal(10,5)'] * 10 + ['decimal(20,8)'] * 10


def quaternion_real(quaternion):
    """Return real part of quaternion.

    >>> quaternion_real([3, 0, 1, 2])
    3.0

    """
    return float(quaternion[0])


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 canvas(with_attribution=True):
    """
    Placeholder function to show example docstring (NumPy format)

    Replace this function and doc string for your own project

    Parameters
    ----------
    with_attribution : bool, Optional, default: True
        Set whether or not to display who the quote is from

    Returns
    -------
    quote : str
        Compiled string including quote and optional attribution
    """
    quote = 'The code is but a canvas to our imagination.'
    if with_attribution:
        quote += '\n\t- Adapted from Henry David Thoreau'
    return quote


@staticmethod
def format_sizeof(num, suffix='', divisor=1000):
    """
        Formats a number (greater than unity) with SI Order of Magnitude
        prefixes.

        Parameters
        ----------
        num  : float
            Number ( >= 1) to format.
        suffix  : str, optional
            Post-postfix [default: ''].
        divisor  : float, optional
            Divisor between prefixes [default: 1000].

        Returns
        -------
        out  : str
            Number with Order of Magnitude SI unit postfix.
        """
    for unit in ['', 'k', 'M', 'G', 'T', 'P', 'E', 'Z']:
        if abs(num) < 999.5:
            if abs(num) < 99.95:
                if abs(num) < 9.995:
                    return '{0:1.2f}'.format(num) + unit + suffix
                return '{0:2.1f}'.format(num) + unit + suffix
            return '{0:3.0f}'.format(num) + unit + suffix
        num /= divisor
    return '{0:3.1f}Y'.format(num) + suffix


def reindex_doc(docid, value):
    """Reindex a document using the (undiscriminated) value"""


def get_prefix(fullname: str) ->str:
    """Drop the final component of a qualified name (e.g. ('x.y' -> 'x')."""
    return fullname.rsplit('.', 1)[0]


def get_metadata_keys(options):
    """ Return a list of metadata keys to be extracted. """
    keyfile = options.get('keyfile')
    if keyfile:
        with open(keyfile, 'r') as mdkeys_file:
            return mdkeys_file.read().splitlines()
    else:
        return None


@classmethod
def check_reserved(cls, name):
    """Check if a player name is reserved.

        :param str name: The player name to check
        :returns bool: True if the name is reserved, else False

        """
    return name in cls.RESERVED


def upgrade():
    """ Runs the upgrade and returns a list of strings containing the result
        """


def no_print(*args, **kwargs):
    """Dummy method for not actually printing anything"""
    pass


def _getAs(args):
    """
    Extract the 'as' alias as provided by the user.
    """
    try:
        return args[args.index('as') + 1]
    except (ValueError, IndexError):
        return None


def getDmax(peaks):
    """needs a doc string"""
    return peaks[0][-2]


def join_path(a, b):
    """
    Join two URL path segments together.
    Unlike ``os.path.join``, this removes leading slashes before joining the
    paths, so absolute paths are appended as if they were relative.

    Synopsis::

        >>> join_path('/', '/abc')
        '/abc'

        >>> join_path('/', '/abc/')
        '/abc/'

        >>> join_path('/foo', '/bar/')
        '/foo/bar/'

        >>> join_path('/foo/', '/bar/')
        '/foo/bar/'

        >>> join_path('/foo', 'bar')
        '/foo/bar'
    """
    if not b:
        return a
    while a and a[-1] == '/':
        a = a[:-1]
    while b and b[0] == '/':
        b = b[1:]
    return a + '/' + b


def match_sort_key(match):
    """Function used to sort matches in chronological order, first by level, then by match set.
        Returns a sorting key value for the provided match model.
    """
    levels = {'qm': 0, 'ef': 1000, 'qf': 2000, 'sf': 3000, 'f': 4000}
    key = levels[match['comp_level']]
    key += 100 * match['set_number'] if match['comp_level'] != 'qm' else 0
    key += match['match_number']
    return key


def printFile(data):
    """ A given file in a directory will be passed on as an argument into this function which will then print each line
        of that file leaving it as is"""
    if isinstance(data, str):
        data = open(data)
    for each_line in data:
        print(each_line, end='')


def flatten_fieldsets(fieldsets):
    """Returns a list of field names from an admin fieldsets structure."""
    field_names = []
    for name, opts in fieldsets:
        for field in opts['fields']:
            if isinstance(field, (list, tuple)):
                field_names.extend(field)
            else:
                field_names.append(field)
    return field_names


def merge(left, right):
    """This is a deep merge implementation.
  It's called on manifest_table dicts with the intent of merging all of
  them into a superset of all of their contents. The tricky part is that
  the values need to be merged as well."""
    merged = left
    for table_key, table_value in right.items():
        if left.get(table_key):
            for key in table_value.keys():
                if left[table_key].get(key):
                    merged[table_key][key] = merged[table_key][key
                        ] or table_value[key]
        else:
            merged[table_key] = right[table_key]
    return merged


def is_heartbeat_enabled(outgoing_heartbeat: int, incoming_heartbeat: int):
    """
    Determine if STOMP heartbeat is enabled or not. Per the specification, it'll only be enabled
    if a both estabilished times is greater than zero.

    More on: https://stomp.github.io/stomp-specification-1.1.html#Heart-beating
    """
    return outgoing_heartbeat > 0 and incoming_heartbeat > 0


def getItalianAccentedVocal(vocal, acc_type='g'):
    """
    It returns given vocal with grave or acute accent
    """
    vocals = {'a': {'g': u'à', 'a': u'á'}, 'e': {'g': u'è', 'a': u'é'}, 'i':
        {'g': u'ì', 'a': u'í'}, 'o': {'g': u'ò', 'a': u'ó'}, 'u': {'g':
        u'ù', 'a': u'ú'}}
    return vocals[vocal][acc_type]


@staticmethod
def string_repr(obj):
    """Just get the class name + object"""
    return str(obj).split(' at ')[0] + '>'


def default_marshaller(obj):
    """
    Retrieve the state of the given object.

    Calls the ``__getstate__()`` method of the object if available, otherwise returns the
    ``__dict__`` of the object.

    :param obj: the object to marshal
    :return: the marshalled object state

    """
    if hasattr(obj, '__getstate__'):
        return obj.__getstate__()
    try:
        return obj.__dict__
    except AttributeError:
        raise TypeError(
            '{!r} has no __dict__ attribute and does not implement __getstate__()'
            .format(obj.__class__.__name__))


@staticmethod
def get_subtype(object_dictionary):
    """
        Given the hash representation of a subtype of this class,
        use the info in the hash to return the class of the subtype.
        """
    type = object_dictionary['sourceType']
    if type == 'BACKUP':
        return 'CreateDbSystemSourceFromBackupDetails'
    if type == 'IMPORTURL':
        return 'CreateDbSystemSourceImportFromUrlDetails'
    else:
        return 'CreateDbSystemSourceDetails'


@classmethod
def new(cls, c, cc=None):
    """Create a new instance of Packet initialized by two bytes: c and cc."""
    if isinstance(c, (tuple, list)):
        c, cc = c
    elif cc is None:
        return cls().write16(c)
    return cls().write8(c).write8(cc)


def UernPremRate():
    """Unearned Premium Rate"""
    return None


def dsquared(v):
    """
    Returns the squared length of a 3-vector. Does not consider PBC.

    Args:
        v: a 3-vector

    Returns:
        the squared length of the vector
    """
    return v[0] ** 2 + v[1] ** 2 + v[2] ** 2


def close(fd):
    """Closes the given file descriptor.

    :param int fd: File descriptor.

    :rtype: None
    """


@classmethod
def is_compatible_class(manager, cls):
    """ is_compatible_class(cls)

        Returns True if the given class is SSDF-compatible.

        """
    return not manager.is_incompatible_class(cls)


def get_globals_dict(plugin):
    """
    Return a dictionary that will be added to the globals dictionary
    when executing calculations.
    """
    res = {}
    res.update(plugin.globals_dict())
    return res


def create_read_list_file(filename):
    """
	Creates a file (including header line) for read list information to be written to and returns
	the file object.
	"""
    f = open(filename, 'w')
    print('#readname', 'source_id', 'sample', 'phaseset', 'haplotype',
        'covered_variants', 'first_variant_pos', 'last_variant_pos', sep=
        '\t', file=f)
    return f


def import_statsd():
    """
    Import only the statd by wolph not the mozilla statsd
    TODO: Move to mozilla statds which is more widely used
    """
    try:
        import django_statsd
        is_wolphs_statsd = hasattr(django_statsd, 'start') and hasattr(
            django_statsd, 'stop')
        if not is_wolphs_statsd:
            django_statsd = None
    except ImportError:
        django_statsd = None
    return django_statsd


def add_log_level(logger, method_name, event_dict):
    """
    Add the log level to the event dict.
    """
    if method_name == 'warn':
        method_name = 'warning'
    event_dict['level'] = method_name
    return event_dict


def sorted_by_name(query):
    """Return ``query`` sorted case-insensitively by object ``name``."""
    return sorted(query.all(), key=lambda obj: obj.name.lower())


def merged_filter_region_generator(filter_region_generator, filter_file,
    filter_function):
    """ Generates merged regions from a given generator that produces
    chromosome regions """
    current_chromosome = None
    current_start = None
    current_end = None
    for chromosome_name, filter_start, filter_end in filter_region_generator(
        filter_file, filter_function):
        if current_chromosome:
            if (chromosome_name == current_chromosome and current_end >=
                filter_start):
                current_end = filter_end
            else:
                yield current_chromosome, current_start, current_end
                current_chromosome = chromosome_name
                current_start = filter_start
                current_end = filter_end
        else:
            current_chromosome = chromosome_name
            current_start = filter_start
            current_end = filter_end
    if current_chromosome:
        yield current_chromosome, current_start, current_end


@staticmethod
def total_length():
    """Returns PDU total length.

        This PDU has a fixed length of 10, so method always returns 10
        regardless of specific instance

        :return: PDU total length
        """
    return 10


@staticmethod
def merge(reports):
    """Works for dictionary reports. Merges the reports list of
        dictionaries in a single dictionary. The list of the first
        dictionary are extended by those of all subsequent lists."""
    report = reports[0]
    keys_report = list(report.keys())
    for other_report in reports[1:]:
        for key in keys_report:
            report[key] += other_report[key]
    return report


def write(sid0_list, sid1_list, pvalue_list, output_file):
    """
    Given three arrays of the same length [as per the output of epistasis(...)], writes a header and the values to the given output file.
    """
    with open(output_file, 'w') as out_fp:
        out_fp.write('{0}\t{1}\t{2}\n'.format('sid0', 'sid1', 'pvalue'))
        for i in range(len(pvalue_list)):
            out_fp.write('{0}\t{1}\t{2}\n'.format(sid0_list[i], sid1_list[i
                ], pvalue_list[i]))


def get_ees(in_meta, in_file=None):
    """
    Extract the *effective echo spacing* :math:`t_\\text{ees}` from BIDS.

    Calculate the *effective echo spacing* :math:`t_\\text{ees}`
    for an input :abbr:`EPI (echo-planar imaging)` scan.

    There are several procedures to calculate the effective
    echo spacing. The basic one is that an ``EffectiveEchoSpacing``
    field is set in the JSON sidecar. The following examples
    use an ``'epi.nii.gz'`` file-stub which has 90 pixels in the
    j-axis encoding direction.

    >>> meta = {'EffectiveEchoSpacing': 0.00059,
    ...         'PhaseEncodingDirection': 'j-'}
    >>> get_ees(meta)
    0.00059

    If the *total readout time* :math:`T_\\text{ro}` (``TotalReadoutTime``
    BIDS field) is provided, then the effective echo spacing can be
    calculated reading the number of voxels :math:`N_\\text{PE}` along the
    readout direction and the parallel acceleration
    factor of the EPI

      .. math ::

           =  T_\\text{ro} \\,  (N_\\text{PE} / f_\\text{acc} - 1)^{-1}

    where :math:`N_y` is the number of pixels along the phase-encoding direction
    :math:`y`, and :math:`f_\\text{acc}` is the parallel imaging acceleration factor
    (:abbr:`GRAPPA (GeneRalized Autocalibrating Partial Parallel Acquisition)`,
    :abbr:`ARC (Autocalibrating Reconstruction for Cartesian imaging)`, etc.).

    >>> meta = {'TotalReadoutTime': 0.02596,
    ...         'PhaseEncodingDirection': 'j-',
    ...         'ParallelReductionFactorInPlane': 2}
    >>> get_ees(meta, in_file='epi.nii.gz')
    0.00059

    Some vendors, like Philips, store different parameter names (see
    http://dbic.dartmouth.edu/pipermail/mrusers/attachments/20141112/eb1d20e6/attachment.pdf
    ):

    >>> meta = {'WaterFatShift': 8.129,
    ...         'MagneticFieldStrength': 3,
    ...         'PhaseEncodingDirection': 'j-',
    ...         'ParallelReductionFactorInPlane': 2}
    >>> get_ees(meta, in_file='epi.nii.gz')
    0.00041602630141921826

    """
    import nibabel as nb
    from sdcflows.interfaces.fmap import _get_pe_index
    ees = in_meta.get('EffectiveEchoSpacing', None)
    if ees is not None:
        return ees
    acc = float(in_meta.get('ParallelReductionFactorInPlane', 1.0))
    npe = nb.load(in_file).shape[_get_pe_index(in_meta)]
    etl = npe // acc
    trt = in_meta.get('TotalReadoutTime', None)
    if trt is not None:
        return trt / (etl - 1)
    wfs = in_meta.get('WaterFatShift', None)
    if wfs is not None:
        fstrength = in_meta['MagneticFieldStrength']
        wfd_ppm = 3.4
        g_ratio_mhz_t = 42.57
        wfs_hz = fstrength * wfd_ppm * g_ratio_mhz_t
        return wfs / (wfs_hz * etl)
    raise ValueError('Unknown effective echo-spacing specification')


def convert_strong(element, text):
    """
    Add '**' to both ends of the text

    Parameters
    ----------
    element : bs4.node
    text : str

    Returns
    -------
    str
        bolded text
    """
    if text:
        text = '**%s**' % text
    return text


def fetchXMLFromDbusObject(obj):
    """
    Return xml of dbus proxy object
    """
    method = obj.get_dbus_method('Introspect', dbus_interface=
        'org.freedesktop.DBus.Introspectable')
    return method()


def get_lat_name(da):
    """
        Returns name of latitude dimension in input array
        Author: Dougie Squire
        Date: 03/03/2018

        Parameters
        ----------
        da : xarray DataArray
            Array with coordinate corresponding to latitude

        Returns
        -------
        name : str
            Name of dimension corresponding to latitude

        Examples
        --------
        >>> A = xr.DataArray(np.random.normal(size=(2,2,2,2,2)),
        ...                  coords=[('lat', np.arange(2)), ('lon', np.arange(2)),
        ...                          ('depth', np.arange(2)), ('level', np.arange(2))])
        >>> doppyo.utils.get_lat_name(A)
        'lat'
    """
    if 'lat' in da.dims:
        return 'lat'
    elif 'lat_2' in da.dims:
        return 'lat_2'
    elif 'yt_ocean' in da.dims:
        return 'yt_ocean'
    else:
        raise KeyError('Unable to determine latitude dimension')
        pass


def _change_metrics(backends):
    """Set a new backend."""
    global _metrics_backends
    _metrics_backends = backends


def check_conversion_support(out_format, supported_formats):
    """
        Confirms it is possible to convert the input into the specified output
        format

        Parameters
        ----------
        out_format : str
            File format of the output
        supported_formats : str
            The file formats the input supports (i.e. allows conversion)

        Raises
        ------
        Exception
            If the output format is not supported
    """
    if out_format not in supported_formats:
        raise Exception('Input cannot be converted into the format specified')


def _parity_ok(z, c, m):
    """Check total electrons (neutral protons `z` and charge `c`) is (un)paired-compatible with multiplicity `m`"""
    return m % 2 != (z - c) % 2


def mimetype(serial):
    """Function to generate a blaze serialization format mimetype put into a
    dictionary of headers for consumption by requests.

    Examples
    --------
    >>> from blaze.server.serialization import msgpack
    >>> mimetype(msgpack)
    {'Content-Type': 'application/vnd.blaze+msgpack'}
    """
    return {'Content-Type': 'application/vnd.blaze+%s' % serial.name}


def CurrentControlledVoltageSource(G, Z0=50.0):
    """ CurrentControlledVoltageSource
    Current controlled voltage source
    @param G float or complex transresistance.
    @param Z0 (optional) float or complex reference impedance Z0 (defaults to 50 ohms).
    @return s-parameter matrix of a current controlled voltage source.
    The four port current controlled voltage source has
    the current sense element sensing the current
    entering port 1 and exiting port 2.

    The voltage generated by the amplifier has its positive terminal
    at port 4 and its negative terminal at port 3.
    """
    return [[0.0, 1.0, 0.0, 0.0], [1.0, 0.0, 0.0, 0.0], [-G / (2.0 * Z0), G /
        (2.0 * Z0), 0.0, 1.0], [G / (2.0 * Z0), -G / (2.0 * Z0), 1.0, 0.0]]


def get_generic_type(obj):
    """Get the generic type of an object if possible, or runtime class otherwise.
    Examples::

        class Node(Generic[T]):
            ...
        type(Node[int]()) == Node
        get_generic_type(Node[int]()) == Node[int]
        get_generic_type(Node[T]()) == Node[T]
        get_generic_type(1) == int
    """
    gen_type = getattr(obj, '__orig_class__', None)
    return gen_type if gen_type is not None else type(obj)


def serialize_uint32(n: int) ->bytes:
    """
    Serialize an unsigned integer ``n`` as 4 bytes (32 bits) in big-endian
    order.

    Corresponds directly to the "ser_32(i)" function in BIP32
    (https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki#conventions).

    :param n: The integer to be serialized.

    :return: A byte sequence containing the serialization of ``n``.
    """
    return n.to_bytes(4, 'big')


def terminate_y_less_zero(x, y, vx, vy, ax, ay, t):
    """Returns True if the y velocity is less than zero, otherwise returns False"""
    return y[-1] < 0


def helical_transition_Re_Seth_Stahel(Di, Dc):
    """Calculates the transition Reynolds number for flow inside a curved or
    helical coil between laminar and turbulent flow, using the method of [1]_.

    .. math::
        Re_{crit} = 1900\\left[1 + 8 \\sqrt{\\frac{D_i}{D_c}}\\right]

    Parameters
    ----------
    Di : float
        Inner diameter of the coil, [m]
    Dc : float
        Diameter of the helix/coil measured from the center of the tube on one
        side to the center of the tube on the other side, [m]

    Returns
    -------
    Re_crit : float
        Transition Reynolds number between laminar and turbulent [-]

    Notes
    -----
    At very low curvatures, converges to Re = 1900.

    Examples
    --------
    >>> helical_transition_Re_Seth_Stahel(1, 7.)
    7645.0599897402535

    References
    ----------
    .. [1] Seth, K. K., and E. P. Stahel. "HEAT TRANSFER FROM HELICAL COILS
       IMMERSED IN AGITATED VESSELS." Industrial & Engineering Chemistry 61,
       no. 6 (June 1, 1969): 39-49. doi:10.1021/ie50714a007.
    """
    return 1900.0 * (1.0 + 8.0 * (Di / Dc) ** 0.5)


@classmethod
def metadata(cls, sobject):
    """
        Extract the metadata from a suds object.
        @param sobject: A suds object
        @type sobject: L{Object}
        @return: The object's metadata
        @rtype: L{sudsobject.Metadata}
        """
    return sobject.__metadata__


def read_all_data(db: dict, record_id: int):
    """
    Return the contents of all data columns for a given record_id
    """
    return_dict = {}
    for data_name in db['db'][str(record_id)]:
        return_dict[data_name] = db['db'][str(record_id)][data_name]
    return return_dict


def map_to_ta_modes(ins, max_group, min_group):
    """Turns the min/max groups into the closest allowable
    TA group mode.

    Parameters
    ----------
    ins : str
        Instrument.
    max_group : int
        The maximum number of groups without oversaturating.

    min_group : int
        The groups needed to hit the target SNR.

    Returns
    -------
    min_ta_groups : int
        The min possible groups to hit target SNR.
    max_ta_groups : int
        The max possible groups before saturation.
    """
    groups = {'miri': [3, 5, 9, 15, 23, 33, 45, 59, 75, 93, 113, 135, 159,
        185, 243, 275, 513], 'niriss': [3, 5, 7, 9, 1, 13, 15, 17, 19],
        'nirspec': [3], 'nircam': [3, 5, 9, 17, 33, 65]}
    allowable_groups = groups[ins]
    min_ta_groups = min(allowable_groups, key=lambda x: abs(x - min_group))
    max_ta_groups = min(allowable_groups, key=lambda x: abs(x - max_group))
    if min_group == 0:
        min_ta_groups = 0
        max_ta_groups = 0
    if min_group > max(allowable_groups):
        min_ta_groups = -1
        max_ta_groups = 0
    return max_ta_groups, min_ta_groups


def ensure_tuple(var):
    """Ensures that variable is a tuple, even if its a scalar"""
    if getattr(var, '__iter__', False):
        return tuple(var)
    return var,


def _convert_hashes(values):
    """Convert Pipfile.lock hash lines into InstallRequirement option format.

    The option format uses a str-list mapping. Keys are hash algorithms, and
    the list contains all values of that algorithm.
    """
    hashes = {}
    if not values:
        return hashes
    for value in values:
        try:
            name, value = value.split(':', 1)
        except ValueError:
            name = 'sha256'
        if name not in hashes:
            hashes[name] = []
        hashes[name].append(value)
    return hashes


def open_python_file(filename):
    """Open a read-only Python file taking proper care of its encoding.

    In Python 3, we would like all files to be opened with utf-8 encoding.
    However, some author like to specify PEP263 headers in their source files
    with their own encodings. In that case, we should respect the author's
    encoding.
    """
    import tokenize
    if hasattr(tokenize, 'open'):
        return tokenize.open(filename)
    else:
        return open(filename, 'r')


@staticmethod
def apply_noop(aggregate, event):
    """
        Aggregate event handler function that acts as a no-op

        Event handlers that do something should return a new aggregate with
        updated state.  For example:

            @staticmethod
            def apply_hello_world(aggregate):
                return aggregate.set_state('hello', 'world')

        Arguments:
        aggregate -- Aggregate instance to which the event will be applied
        event -- Event instance to "apply" to the `aggregate`
        """
    return aggregate


def __init__(símismo, nombre, proyecto):
    """
        Creamos un Coso con un numbre y, posiblemente, una fuente de cual cargarlo.

        :param nombre: El nombre del Coso
        :type nombre: str

        :param proyecto: Si este Coso hace parte de un proyecto (se creará, si necesario, el archivo apropiado
        para guardarlo).
        :type proyecto: str

        """
    símismo.receta = dict(coefs={}, estr={}, info={})
    símismo.proyecto = proyecto
    símismo.nombre = nombre
    símismo.objetos = []


@staticmethod
def ext_make(frame):
    """Tries to load the given Frame from cache or makes it and returns
        the rendered Frame.
        """
    frame.make()
    return frame


def inch_to_EMU(value):
    """1 inch = 914400 EMUs"""
    return int(value * 914400)


@classmethod
def get_structure(cls):
    """
        Returns a dictionary with model field objects.

        :return: dict
        """
    return cls.__structure__.copy()


def update_project(p_dict, slug):
    """Updates project by slug"""
    updated_param = {'uri': p_dict['uri'] if 'uri' in p_dict else None,
        'name': p_dict['name'] if 'name' in p_dict else 'TimeSync API',
        'slugs': p_dict['slugs'] if 'slugs' in p_dict else [slug],
        'created_at': '2014-04-16', 'updated_at': '2014-04-18',
        'deleted_at': None, 'uuid': '309eae69-21dc-4538-9fdc-e6892a9c4dd4',
        'revision': 2, 'users': p_dict['users'] if 'users' in p_dict else {
        'patcht': {'member': True, 'spectator': False, 'manager': False},
        'tschuy': {'member': True, 'spectator': True, 'manager': True}}}
    return updated_param


def validate_data_privacy(request, data, wrapper_kw=None):
    """ Validate :data: contains only data allowed by privacy settings.

    :param request: Pyramid Request instance
    :param data: Dict containing request/response data which should be
        validated
    """
    from nefertari import wrappers
    if wrapper_kw is None:
        wrapper_kw = {}
    wrapper = wrappers.apply_privacy(request)
    allowed_fields = wrapper(result=data, **wrapper_kw).keys()
    data = data.copy()
    data.pop('_type', None)
    not_allowed_fields = set(data.keys()) - set(allowed_fields)
    if not_allowed_fields:
        raise wrappers.ValidationError(', '.join(not_allowed_fields))


def id2z(pid):
    """
    Given a nucleus ID (2006 PDG standard), returns the charge number.

    :param pid: nucleus ID
    :return: charge number
    """
    return pid % 1000000000 // 10000


def total_return(returns):
    """
    Compute total return from simple returns.

    Parameters
    ----------
    returns : pd.Series of periodic returns

    Returns
    -------
    total_returns : array-like
        Series of total returns.
    """
    if len(returns) < 1:
        return returns.copy()
    result = returns.copy()
    result = result.add(1, fill_value=0)
    result = result.prod(skipna=True)
    result = result.add(-1)
    return result


def special_path_string(path_string):
    """ Replace the path string . -> /
    rather than the following cases
    .
    ..
    path/./../
    """
    if path_string.startswith('.'):
        return path_string
    if path_string.find('/.') != -1:
        return path_string
    return path_string.replace('.', '/')


def age_frequencies():
    """Call to periodically adjust the frequencies of items."""


def copy_edges(g, graph1, name=None, **kwd):
    """
    .. versionadded:: 0.1.12

    Used by :func:`link_nodes` to copy edges
    """
    if name is None:
        name = graph1.name
    for node1, node2, data in graph1.edges_iter(data=True):
        data = data.copy()
        data.update(kwd)
        g.add_edge('{0}_{1}'.format(name, node1), '{0}_{1}'.format(name,
            node2), **data)


@staticmethod
def positional_to_model(value):
    """Converts a positional argument to a model value"""
    return value


def bbox_corners_to_coco(bbox):
    """Convert bbox from ``[minx, miny, maxx, maxy]`` to coco format.

    COCO formats bounding boxes as ``[minx, miny, width, height]``.

    Arguments
    ---------
    bbox : :class:`list`-like of numerics
        A 4-element list of the form ``[minx, miny, maxx, maxy]``.

    Returns
    -------
    coco_bbox : list
        ``[minx, miny, width, height]`` shape.
    """
    return [bbox[0], bbox[1], bbox[2] - bbox[0], bbox[3] - bbox[1]]


def __adjust_rad_for_skip(diam, skip):
    """

    If necessary, increase the crop circle diameter (radius) for the chosen
    skip value. This is required because the diameter must be divisible by
    skip without rest.

    Parameters
    ----------
    diam: int
        Initial diameter of crop circle (in pixels).
    skip: int
        The numbers of pixels to skip when evaluating film thickness
        information.

    Returns
    -------
    diam: int
        Diameter of circle that is divisible by skip without rest.

    """
    while diam % skip != 0:
        diam += 1
    return diam


def adoptStreamPort(fileDescriptor, addressFamily, factory):
    """
        Add an existing listening I{SOCK_STREAM} socket to the reactor to
        monitor for new connections to accept and handle.

        @param fileDescriptor: A file descriptor associated with a socket which
            is already bound to an address and marked as listening.  The socket
            must be set non-blocking.  Any additional flags (for example,
            close-on-exec) must also be set by application code.  Application
            code is responsible for closing the file descriptor, which may be
            done as soon as C{adoptStreamPort} returns.
        @type fileDescriptor: C{int}

        @param addressFamily: The address family (or I{domain}) of the socket.
            For example, L{socket.AF_INET6}.

        @param factory: A L{ServerFactory} instance to use to create new
            protocols to handle connections accepted via this socket.

        @return: An object providing L{IListeningPort}.

        @raise UnsupportedAddressFamily: If the given address family is not
            supported by this reactor, or not supported with the given socket
            type.

        @raise UnsupportedSocketType: If the given socket type is not supported
            by this reactor, or not supported with the given socket type.
        """


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

        :param SharedLinkFileInfo val:
        :rtype: PathOrLink
        """
    return cls('link', val)


def get_nt_nchans_for_out(out, timit_pitch):
    """Returns number of time points and channels for one subject

    out is returned from timit.load_h5py_out(subject_number) and contains TIMIT data for one subject.

    This function goes through each TIMIT trial to determine how many timepoints of data are there.
    """
    nt = 0
    for trial in out:
        nchans = out[trial]['ecog'].shape[0]
        timit_name = out[trial].attrs['timit_name'][0]
        for i in range(out[trial]['ecog'].shape[2]):
            nt = nt + timit_pitch.loc[timit_name].pitch.shape[0]
    return nt, nchans


def stringify_location(chrom, start, stop, strand, region=None):
    """Convert genome location to a string, optionally prefixing with region"""
    if region is not None:
        return '{0}:{1}:{2}-{3}:{4}'.format(region, chrom, start, stop, strand)
    else:
        return '{0}:{1}-{2}:{3}'.format(chrom, start, stop, strand)


@classmethod
def extract_audio_features(cls, conn, table, frame_shift=10, frame_length=
    25, n_bins=40, n_ceps=40, feature_scaling_method='STANDARDIZATION',
    n_output_frames=500, casout=None, random_shuffle=True, **kwargs):
    """
        Extracts audio features from the audio files

        Parameters
        ----------
        conn : CAS
            A connection object to the current session.
        table : AudioTable
            An audio table containing the audio files.
        frame_shift : int, optional
            Specifies the time difference (in milliseconds) between the beginnings of consecutive frames.
            Default: 10
        frame_length : int, optional
            Specifies the length of a frame (in milliseconds).
            Default: 25
        n_bins : int, optional
            Specifies the number of triangular mel-frequency bins.
            Default: 40
        n_ceps : int, optional
            Specifies the number of cepstral coefficients in each MFCC feature frame (including C0).
            Default: 40
        feature_scaling_method : string, optional
            Specifies the feature scaling method to apply to the computed feature vectors.
            Default: 'standardization'
        n_output_frames : int, optional
            Specifies the exact number of frames to include in the output table (extra frames are dropped and missing
            frames are padded with zeros).
            Default: 500
        casout : dict or string or CASTable, optional
            CAS Output table
        random_shuffle : bool, optional
            Specifies whether shuffle the generated CAS table randomly.
            Default: True
        kwargs : keyword-arguments, optional
            Additional parameter for feature extraction.

        Returns
        -------
        :class:`AudioTable`
            If table exists
        None
            If no table exists

        """
    return cls.__extract_audio_features(conn, table, frame_shift,
        frame_length, n_bins, n_ceps, feature_scaling_method,
        n_output_frames, casout, 0, random_shuffle, **kwargs)


def get_sanitised_resp_msg(exp):
    """Get the sanitise response messages from a calendar response

    Returns the sanitised message, with any character not in the whitelist replaced by '_'
    """
    WHITELIST = (
        b'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789#-.,; '
        )
    raw_msg = bytearray(exp.read(160))
    for i in range(len(raw_msg)):
        if raw_msg[i] not in WHITELIST:
            raw_msg[i] = ord('_')
    return raw_msg.decode()


def lines(value):
    """ Split a string on its newlines

    RawConfigParser supports "continuations in the style of RFC822", which
    gives us a very natural way of having values that are lists of strings.

    If value isn't a string, leaves it alone.
    """
    try:
        value.split
    except AttributeError:
        return value
    else:
        return value.split('\n')


def delete_portfolio(AcceptLanguage=None, Id=None):
    """
    Deletes the specified portfolio. This operation will not work with a portfolio that has been shared with you or if it has products, users, constraints, or shared accounts associated with it.
    See also: AWS API Documentation

    :example: response = client.delete_portfolio(
        AcceptLanguage='string',
        Id='string'
    )

    :type AcceptLanguage: string
    :param AcceptLanguage: The language code to use for this operation. Supported language codes are as follows:
            'en' (English)
            'jp' (Japanese)
            'zh' (Chinese)
            If no code is specified, 'en' is used as the default.

    :type Id: string
    :param Id: [REQUIRED]
            The identifier of the portfolio for the delete request.

    :rtype: dict
    :return: {}

    :returns:
    (dict) --

    """
    pass


def match_any(res, string):
    """Return if a string matches any of a list of regular expressions.

	Args:
		string (str): The string that you want to check.
		res: An Iterable of regular expressions to check.

	Returns:
		True if any of the regexes in res matches string, otherwise False.
	"""
    for regex in res:
        if regex.match(string):
            return True
    return False


def _decode(reader, filename, **kwargs):
    """Successively try different methods to open ``filename`` with
    ``reader``."""
    try:
        return reader(**kwargs)
    except TypeError:
        pass
    try:
        return reader(filename, **kwargs)
    except TypeError:
        pass
    try:
        with open(filename, 'r') as fileobj:
            return reader(fileobj, **kwargs)
    except UnicodeDecodeError:
        with open(filename, 'rb') as fileobj:
            return reader(fileobj, **kwargs)


def join(path1, path2):
    """
    nicely join two path elements together
    """
    if path1.endswith('/') and path2.startswith('/'):
        return ''.join([path1, path2[1:]])
    elif path1.endswith('/') or path2.startswith('/'):
        return ''.join([path1, path2])
    else:
        return ''.join([path1, '/', path2])


def createFile(content, fname):
    """ write a string to a file """
    file = open(fname, 'w')
    file.write(content)
    file.close()


def moveObjectsToBottom(ids, subset_ids=None):
    """ Move specified sub-objects to bottom of container. """


def lower_camel_case_from_underscores(string):
    """Generate a lower-cased camelCase string from an underscore_string"""
    components = string.split('_')
    string = components[0].lower()
    for component in components[1:]:
        string += component[0].upper() + component[1:].lower()
    return string


def plus_or_dot(pieces):
    """Return a + if we don't already have one, else return a ."""
    if '+' in pieces.get('closest-tag', ''):
        return '.'
    return '+'


@staticmethod
def _parse_statistic(data, scale):
    """Parse binary statistics returned from the history API"""
    i = 0
    for byte in bytearray(data):
        i = (i << 8) + byte
    if i == 32768:
        return None
    if scale == 0:
        return i
    return float(i) / (scale * 10)


def bindVarCompare(a, b):
    """
    _bindVarCompare_

    Bind variables are represented as a tuple with the first element being the
    variable name and the second being it's position in the query.  We sort on
    the position in the query.
    """
    if a[1] > b[1]:
        return 1
    elif a[1] == b[1]:
        return 0
    else:
        return -1


def setup_subsubparser_makeref(parser):
    """ argparse for pybombs git make-ref """
    parser.add_argument('packages', help=
        'List of packages to add to git reference', nargs='*')


@staticmethod
def selector(event, field_spec=None):
    """
        Function to select specific fields of an event using
        a fieldSpec and return a new event with just those fields.

        The fieldSpec currently can be:

        * A single field name
        * An list of field names

        The function returns a new event.

        Parameters
        ----------
        event : Event
            Event to pull from.
        field_spec : str, list, tuple, None, optional
            Column or columns to look up. If you need to retrieve multiple deep
            nested values that ['can.be', 'done.with', 'this.notation'].
            A single deep value with a string.like.this.  If None, the default
            column 'value' will be used.

        Returns
        -------
        Event
            A new event object.
        """
    new_dict = dict()
    if isinstance(field_spec, str):
        new_dict[field_spec] = event.get(field_spec)
    elif isinstance(field_spec, (list, tuple)):
        for i in field_spec:
            if isinstance(i, str):
                new_dict[i] = event.get(i)
    elif field_spec is None:
        new_dict['value'] = event.get()
    else:
        return event
    return event.set_data(new_dict)


@classmethod
def create_repositioning_command(cls, position=None):
    """Create node interpretable as a command to change the current
        position

        :type position:
        """
    return cls(type_=cls.CHANGE_POSITION, position=position)


def header_line(length, border='-'):
    """Return header of length"""
    return border * length


@classmethod
def classpath_contents(cls, targets, classpath_products, confs=('default',)):
    """Provide a generator over the contents (classes/resources) of a classpath.

        :param targets: Targets to iterate the contents classpath for.
        :param ClasspathProducts classpath_products: Product containing classpath elements.
        :param confs: The list of confs for use by this classpath.
        :returns: An iterator over all classpath contents, one directory, class or resource relative
                  path per iteration step.
        :rtype: :class:`collections.Iterator` of string
        """
    classpath_iter = cls._classpath_iter(classpath_products.get_for_targets
        (targets), confs=confs)
    for f in cls.classpath_entries_contents(classpath_iter):
        yield f


def DBG(text):
    """DBG - temporary debugging function

    Args:
        text (str): text to print
    """
    print(f'LEP: {text}')


def xsd_error_log_string(xsd_error_log):
    """Return a human-readable string representation of the error log
    returned by lxml's XMLSchema validator.
    """
    ret = []
    for error in xsd_error_log:
        ret.append('ERROR ON LINE {}: {}'.format(error.line, error.message.
            encode('utf-8')))
    return '\n'.join(ret)


def rep_newlines_with_space(string: str) ->str:
    """Removes newlines and replaces them with spaces.

    Also reduces double spaces to single spaces.
    """
    return string.replace('\n', ' ').replace('  ', ' ')


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 step_stat(p, step=0, **kwargs):
    """Register the number of steps done in `state` for `p`"""
    step += 1
    return {'step': step}


@classmethod
def get_content(cls, abspath, start=None, end=None):
    """Retrieve the content of the requested resource which is located
        at the given absolute path.
        This class method may be overridden by subclasses.  Note that its
        signature is different from other overridable class methods
        (no ``settings`` argument); this is deliberate to ensure that
        ``abspath`` is able to stand on its own as a cache key.
        This method should either return a byte string or an iterator
        of byte strings.  The latter is preferred for large files
        as it helps reduce memory fragmentation.
        .. versionadded:: 3.1
        """
    with open(abspath, 'rb') as file:
        if start is not None:
            file.seek(start)
        if end is not None:
            remaining = end - (start or 0)
        else:
            remaining = None
        while True:
            chunk_size = 64 * 1024
            if remaining is not None and remaining < chunk_size:
                chunk_size = remaining
            chunk = file.read(chunk_size)
            if chunk:
                if remaining is not None:
                    remaining -= len(chunk)
                yield chunk
            else:
                if remaining is not None:
                    assert remaining == 0
                return


def str_presenter(dumper, data):
    """Patch YAML so that it folds the long query strings."""
    if len(data.splitlines()) > 1:
        return dumper.represent_scalar('tag:yaml.org,2002:str', data, style='|'
            )
    return dumper.represent_scalar('tag:yaml.org,2002:str', data)


@classmethod
def register_plugins(cls, plugins):
    """
        Reguster plugins. The plugins parameter should be dict mapping model to plugin.

        Just calls a register_plugin for every such a pair.
        """
    for model in plugins:
        cls.register_plugin(model, plugins[model])


def add_basin_outlines(mydem, fig, ax, size_outline=1, zorder=2, color='k'):
    """
		Add catchment outlines to any axis given with its associated LSDDEM object ofc.
		No need to mention that catchment needs to be extracted within the LSDDEM object beforehand.
		Arguments:
			fig: the matplotlib figure
			ax: the matplotlib ax
			size_outline: size (in matplotlib units) of the outline
			zorder: the matplotlib zorder to use
			color (any color code compatible with matplotlib): the color of the outline. Default is black
		Returns:
			Nothing, It directly acts on the figure passed in argument
		Authors:
			B.G.
		Date:
			12/2018 (last update on the 23/02/2018)
	"""
    outlines = mydem.cppdem.get_catchment_perimeter()
    for key, val in outlines.items():
        print(key)
        ax.scatter(val['x'], val['y'], c=color, s=size_outline, zorder=
            zorder, lw=0)


def InterleaveWithTags(text, eType, tagIDs, placeholder):
    """Splits the text at the placeholder string and inserts html-elements of the given type with the given tagIDs as 'HGID's. Most of the time span and div are use, depending on whether the element is supposed to be inline or separated. May fail if number of tagIDs and placeholders in the text not the same."""
    taskParts = text.split(placeholder)
    pos = 0
    text = ''
    while pos < len(tagIDs):
        text += taskParts[pos]
        text += '<' + eType + ' HGID="' + str(tagIDs[pos]
            ) + '"></' + eType + '>'
        pos = pos + 1
    text += taskParts[-1]
    return text


def build_attributes(attributes, obj):
    """Build the Attribute list for the given object."""
    for attr in attributes:
        yield {'name': attr[0], 'object': getattr(attr[2], attr[0]),
            'defining_class': attr[2]}


def mini_sector_size(node=None, parent=None, attr_index=None, rawdata=None,
    new_value=None, **kwargs):
    """Size getter for the getting byte size of a sector in the miniFAT."""
    if new_value is None:
        if 'mini_sector_shift' in kwargs:
            return 1 << kwargs['mini_sector_shift']
        try:
            return 1 << parent.get_root().data.header.mini_sector_shift
        except AttributeError:
            return 0


def check_tuple_size(tuples, tuple_size, context):
    """Helper function to check that the number of points in each tuple is
  equal to tuple_size (e.g. 2 for pairs), and raise a `ValueError` otherwise"""
    if tuple_size is not None and tuples.shape[1] != tuple_size:
        msg_t = (
            """Tuples of {} element(s) expected{}. Got tuples of {} element(s) instead (shape={}):
input={}.
"""
            .format(tuple_size, context, tuples.shape[1], tuples.shape, tuples)
            )
        raise ValueError(msg_t)


def description(filename):
    """Provide a short description."""
    with open(filename) as fp:
        for lineno, line in enumerate(fp):
            if lineno < 3:
                continue
            line = line.strip()
            if len(line) > 0:
                return line


def _fileobj_to_fd(fileobj):
    """ Return a file descriptor from a file object. If
    given an integer will simply return that integer back. """
    if isinstance(fileobj, int):
        fd = fileobj
    else:
        try:
            fd = int(fileobj.fileno())
        except (AttributeError, TypeError, ValueError):
            raise ValueError('Invalid file object: {0!r}'.format(fileobj))
    if fd < 0:
        raise ValueError('Invalid file descriptor: {0}'.format(fd))
    return fd


def updateLogEofX(logEoft, logEofX):
    """
	as above but for log space
	"""
    return logEoft + logEofX


def update(seq):
    """Add the items from the given sequence to the set."""


@classmethod
def get_preferred_strategy(cls, strategy1, strategy2):
    """Get preferred strategy in case of multiple migrations being done at once.

        Chose strategy with higher priority - if two migrations are done at the same time one marked as IGNORE another
        as RETRY, we have to retry flow.

        :param strategy1: the first strategy to compare with
        :type strategy1: TaintedFlowStrategy
        :param strategy2: the second strategy to compare with
        :type strategy2: TaintedFlowStrategy
        :return: strategy with higher priority
        :rtype: TaintedFlowStrategy
        """
    return max(strategy1, strategy2)


def partition(N, k):
    """Distribute ``N`` into ``k`` parts such that each part
    takes the value ``N//k`` or ``N//k + 1`` where ``//`` denotes integer
    division; i.e., perform the minimal lexicographic integer partition.

    Example: N = 5, k = 2  -->  return [3, 2]

    """
    out = [N // k] * k
    remainder = N % k
    for i in range(remainder):
        out[i] += 1
    return out


def copy():
    """Copy selected keyframes to the copy/paste buffer

    """
    pass


def global_coords(x_in, y_in, coeffs):
    """
    Transform pixel coordinates into projected coords using affine transformation coefficients

    Assumes input pixel coordinates refer to the center of the pixel, and so 0.5 is
    added to the inputs.

    @param x_in: X pixel coordinates
    @param y_in: Y pixel coordinates
    @param coeffs: Affine transformation coefficients

    @return projected coordinates
    """
    x = x_in + 0.5
    y = y_in + 0.5
    return coeffs[0] + coeffs[1] * x + coeffs[2] * y, coeffs[3] + coeffs[4
        ] * x + coeffs[5] * y


def _find_corresponding_multicol_key(key, keys_multicol):
    """Find the corresponding multicolumn key."""
    for mk in keys_multicol:
        if key.startswith(mk) and 'of' in key:
            return mk
    return None


@staticmethod
def _drop_int_axes(indexed_shape, int_axes):
    """drop the axis of indexed_shape corresponding to int axes"""
    bcast_shape = []
    for i, size in enumerate(indexed_shape):
        if i not in int_axes:
            bcast_shape.append(size)
    if not bcast_shape:
        bcast_shape = [1]
    return tuple(bcast_shape)


def compile_model(model, optimizer, loss=None, **kwargs):
    """
    Compiles a Keras model

    :param model: A tensorflow model
    :param optimizer: The optimizer to use when compiling
    :param loss: The loss function to use when compiling
    :param kwargs: Additional arguments to pass to the compiler
    :return: a (compiled) Keras model
    """
    model.compile(optimizer=optimizer, loss=loss, **kwargs)
    return model


def shift(a, b, c):
    """ The shift function returns the values as tuple."""
    return a, b, c


@classmethod
def from_mapping(cls, x0, x1):
    """ Create an STTransform from the given mapping

        See `set_mapping` for details.

        Parameters
        ----------
        x0 : array-like
            Start.
        x1 : array-like
            End.

        Returns
        -------
        t : instance of STTransform
            The transform.
        """
    t = cls()
    t.set_mapping(x0, x1)
    return t


def get_full_spec_file_name(default_fsf, l):
    """ Get full-spec filename for given language

    * General spec is always named exactly as ``default_fsf``
    * If there's at least one ``.`` in ``default_fsf``, then the right-most one
      is replaced by ``.{language}.``, e.g. ``full_spec.yaml`` becomes
      ``full_spec.java.yaml``
    * If there's no dot in ``default_fsf``, then ``.{language}`` is appended,
      e.g. ``full_spec`` becomes ``full_spec.java``

    :param default_fsf: a filename for the general spec
    :type default_fsf: ``str``
    :param l: language to construct the spec name for
    :type l: ``str`` or ``NoneType``
    """
    if l is None:
        return default_fsf
    if '.' in default_fsf:
        return default_fsf[::-1].replace('.', '.{}.'.format(l[::-1]), 1)[::-1]
    return '{}.{}'.format(default_fsf, l)


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 wordstartfunc(stc, letter):
    """Prints the words in the given sentence that start with the given letter"""
    for word in stc.split():
        if word[0].lower() == letter.lower():
            print(word)


def swapelem(obj, i, j):
    """Swaps element having index i with
    element having index j in object obj IN PLACE.

    E.g.
    >>> L = ['a', 'b', 'c']
    >>> spwapelem(L, 1, 2)
    >>> print(L)
        ['a', 'c', 'b']

    """
    if len(obj) > 1:
        buf = obj[i]
        obj[i] = obj[j]
        obj[j] = buf


def dotDotRange(dotDotString):
    """
    A method for generating integers.
    For example:

> for i in util.dotDotRange("1..4,100,-1..1"): print i
1
2
3
4
100
-1
0
1

    """
    for intervalString in dotDotString.split(','):
        parts = intervalString.split('..')
        if len(parts) > 2:
            raise Exception("Expect at most one '..' between commas. (see {0})"
                .format(intervalString))
        start = int(parts[0])
        if len(parts) == 1:
            yield start
        else:
            lastInclusive = int(parts[1])
            for i in range(start, lastInclusive + 1):
                yield i


def print_columns(*args):
    """Prints data in fixed-width columns.

    This function takes an arbitrarily long number of args consisting of
    unicodes and/or tuples, then uses PrettyTable to print them into
    columns. Tuples must be of the format (unicode, int). If an arg is an
    unicode, it is simply printed in a column where the column width is
    equal to the width of the unicode. If an arg is a tuple, the first item
    (unicode) of the tuple is printed in a column where the width of the
    column is equal to the second item (int) of the tuple. arg order
    determines column order, i.e. the first arg is printed in the first column.

    Args:
        *args: arbitrary numbers of tuples and unicodes to print,
               see above for details

    Raises:
        ValueError: raised if a tuple cannot be printed

    Example:
        .. code-block:: Python

            >>> print_columns(('hello', 5), 'bye', ('nope', 3))
            hello  bye  nop
                         e
    """


def append_csv(path, lst, sep=','):
    """
        Append jobs to csv file.
        """
    with open(path, 'a') as f:
        print(*lst, sep=sep, file=f)


@classmethod
def isspace(cls, nextchar):
    """ Whether the next character is whitespace """
    return nextchar.isspace()


def _get_session_performance(md, ses_data):
    """Get performance about the session from bpod data"""
    if not ses_data:
        return None, None
    n_trials = ses_data[-1]['trial_num']
    assert len(ses_data) == n_trials
    if 'habituationChoiceWorld' in md['PYBPOD_PROTOCOL']:
        n_correct_trials = 0
    else:
        n_correct_trials = ses_data[-1]['ntrials_correct']
    return n_trials, n_correct_trials


def footer() ->str:
    """
    Return the footer of the LaTeX document.

    :return: LaTeX document footer.
    """
    return '\\end{tikzcd}\n\\end{document}'


@classmethod
def update_json_dict(cls, json_dict):
    """ Update a JSON dictionary before being parsed with class model.
            JSON dictionary is passed by class method from_dict() (see below), and is guaranteed to contain
            at least all expected model keys. Some keys may be associated to None if initial JSON dictionary
            did not provide values for them.

            :param json_dict: a JSON dictionary to be parsed.
            :type json_dict: dict
        """


def pick_type(types):
    """ if there is only one type found
    in a column, then use that. if multiple
    types are found, default back to string.
    """
    type_set = set(types)
    if len(type_set) == 1:
        return list(type_set)[0]
    elif set(['IntType', 'FloatType']) == type_set:
        return 'FloatType'
    else:
        return 'StringType'


def tuple_to_clockface(clock_tuple):
    """ Returns tuple of the time in a (('hour:minutes:seconds'), ms) format.

    Parameters:
    clock_tuple: int
        tuple containing (hours, minutes, sec, ms).
    """
    formatted_time = '%02d:%02d:%02d' % (clock_tuple[0], clock_tuple[1],
        clock_tuple[2])
    formatted_milliseconds = '%03d' % clock_tuple[3]
    return formatted_time, formatted_milliseconds


def parse_positional_args(positional_args):
    """
    [TODO] really? It's so complicated?..
    """
    contexts = []
    contexts_args = []
    build_args = {}
    context_args = {}
    for i in positional_args:
        if i.startswith('ARG:'):
            arg = i.replace('ARG:', '')
            build_args.update([arg.split('=', 1)])
        else:
            docker_build = {}
            context_args = {}
            docker_build['context'] = i
            if positional_args.index(i) > 0:
                context_args['args'] = build_args
                build_args = {}
                contexts_args.append(context_args)
            contexts.append(docker_build)
    if len(build_args) > 0:
        context_args = {}
        context_args['args'] = build_args
        contexts_args.append(context_args)
    i = 0
    for item in contexts:
        if i < len(contexts_args):
            item.update(contexts_args[i])
        else:
            item.update({'args': {}})
        i += 1
    return contexts


def starSC(m=5):
    """ Worst case state complexity for star
    :arg m: number of states
    :type m: integer
    :returns: state complexity
    :rtype: integer"""
    if m > 1:
        return 3 * 2 ** (m - 2)
    return 1


def relative_script(lines):
    """Return a script that'll work in a relocatable environment."""
    activate = (
        "import os; activate_this=os.path.join(os.path.dirname(os.path.realpath(__file__)), 'activate_this.py'); exec(compile(open(activate_this).read(), activate_this, 'exec'), dict(__file__=activate_this)); del os, activate_this"
        )
    activate_at = None
    for idx, line in reversed(list(enumerate(lines))):
        if line.split()[:3] == ['from', '__future__', 'import']:
            activate_at = idx + 1
            break
    if activate_at is None:
        activate_at = 1
    return lines[:activate_at] + ['', activate, ''] + lines[activate_at:]


def guardar_texto(texto, archivo):
    """Guarda texto en un archivo.

    Parameters
    ----------
    texto : str
        Texto que se quiere guardar.
    archivo : str | Path
        Ruta del archivo en el cual se quiere guardar texto.
    """
    with open(archivo, 'w', newline='\n', encoding='utf-8') as ruta:
        for fila in texto.splitlines():
            ruta.write(fila)
            ruta.write('\n')


def describe_instance_patches(InstanceId=None, Filters=None, NextToken=None,
    MaxResults=None):
    """
    Retrieves information about the patches on the specified instance and their state relative to the patch baseline being used for the instance.
    See also: AWS API Documentation

    :example: response = client.describe_instance_patches(
        InstanceId='string',
        Filters=[
            {
                'Key': 'string',
                'Values': [
                    'string',
                ]
            },
        ],
        NextToken='string',
        MaxResults=123
    )

    :type InstanceId: string
    :param InstanceId: [REQUIRED]
            The ID of the instance whose patch state information should be retrieved.

    :type Filters: list
    :param Filters: Each entry in the array is a structure containing:
            Key (string, between 1 and 128 characters)
            Values (array of strings, each string between 1 and 256 characters)
            (dict) --Defines a filter used in Patch Manager APIs.
            Key (string) --The key for the filter.
            Values (list) --The value for the filter.
            (string) --

    :type NextToken: string
    :param NextToken: The token for the next set of items to return. (You received this token from a previous call.)

    :type MaxResults: integer
    :param MaxResults: The maximum number of patches to return (per page).

    :rtype: dict
    :return: {
        'Patches': [
            {
                'Title': 'string',
                'KBId': 'string',
                'Classification': 'string',
                'Severity': 'string',
                'State': 'INSTALLED'|'INSTALLED_OTHER'|'MISSING'|'NOT_APPLICABLE'|'FAILED',
                'InstalledTime': datetime(2015, 1, 1)
            },
        ],
        'NextToken': 'string'
    }

    """
    pass


@staticmethod
def get_connection_type():
    """
        This is needed so the build system knows what
        connection type this connection is considered.
        return::str The socket type of this connection.
        """
    return 'publisher'


def __getIPv4Addr__(addr_list):
    """
    return first ip4 addr it hits
    """
    assert isinstance(addr_list, list)
    for addr in addr_list:
        try:
            assert len(addr[0].split('.')) == 4
            return addr
        except:
            continue
    return None


def remove_last_slash(path):
    """ Remove the last slash in the given path [if any] """
    if path.endswith('/'):
        path = path[:-1]
    return path


def transforms_clear():
    """Reset location, rotation, and scaling of selected bones to their default values

    """
    pass


def full_to_half(s):
    """
    Convert full-width character to half-width one
    将全角字符转换为半角字符
    """
    n = []
    for char in s:
        num = ord(char)
        if num == 12288:
            num = 32
        elif 65281 <= num <= 65374:
            num -= 65248
        char = chr(num)
        n.append(char)
    return ''.join(n)


def load_ceODBC(finder, module):
    """the ceODBC module implicitly imports both datetime and decimal; make
       sure this happens."""
    finder.IncludeModule('datetime')
    finder.IncludeModule('decimal')


def interpolation(x0, y0, x1, y1, x):
    """
    Perfomers interpolation and extrapolation.

    Parameters
    ----------
    x0 : float.
        The coordinate of the first point on the x axis.

    y0 : float.
        The coordinate of the first point on the y axis.

    x1 : float.
        The coordinate of the second point on the x axis.

    y1 : float.
        The coordinate of the second point on the y axis.

    x : float.
        A value in the interval (x0, x1).

    Returns
    -------
    y : float.
        Is the interpolated  or extrapolated value.

    Examples
    --------
    interpolation 1: (30, 3, 40, 5, 37) -> 4.4
    interpolation 2: (30, 3, 40, 5, 35) -> 4.0
    extrapolation 1: (30, 3, 40, 5, 25) -> 2.0
    extrapolation 2: (30, 3, 40, 5, 45) -> 6.0
    """
    y = y0 + (y1 - y0) * ((x - x0) / (x1 - x0))
    return y


def _convert_method_arg(method):
    """
    Convert argument `method` to a function that can be called after
    resampling or reindexing a DataFrame. If `method` is a string such
    as 'ffill' or 'bfill' then it will be converted to a function. Otherwise
    if `method` is itself a callable function, then it will be used directly.

    For example, the string 'ffill' is converted to a lambda-function `func`
    so that `func(df.resample(rule))` actually performs the chained
    function-call `df.resample(rule).ffill()`

    :param method: String or callable function.

    :return: Callable function.
    """
    if method == 'ffill':
        func = lambda x: x.ffill()
    elif method == 'bfill':
        func = lambda x: x.bfill()
    elif method == 'linear':
        func = lambda x: x.interpolate(method='linear')
    elif method == 'quadratic':
        func = lambda x: x.interpolate(method='quadratic')
    elif method == 'mean':
        func = lambda x: x.mean()
    elif callable(method):
        func = method
    else:
        msg = (
            'arg `method` must either be a valid string or a callable function.'
            )
        raise ValueError(msg)
    return func


@classmethod
def compare(cls, a: float, b: float) ->int:
    """
        Return the signed difference between two angles (b-a), accounting
        for the
        fact that the shortest difference between them may be in different
        quadrants.
        :param a: the target angle
        :param b: the starting angle
        :return: (a-b) such that normalizeAngle(b + (a-b)) ==
        normalizeAngle(a)
               and -math.pi < (a-b) <=math.pi
        """
    d = a - b
    if abs(d) < cls.ATOL:
        return 0
    return -1 if d < 0 else 1


def _day2filenum(interval, day):
    """
    Convert a date to corresponding file number.

    Results depend on file interval- weekly, daily, semi-monthly.

    Parameters
    ----------
    interval : string
        The file interval.  Daily, weekly, or semi-monthly accepted,
        represented by lower case first letter.
    day : string
        A number indicating the date.

    Returns
    -------
    filenum : string
        The number of the file within the month of meteorology.
    """
    if interval == 'w':
        filenum = str((int(day) - 1) // 7 + 1)
    elif interval == 's':
        filenum = str((int(day) - 1) // 15 + 1)
    elif interval == 'd' or interval == 'm':
        filenum = day
    else:
        raise ValueError('Meteorology interval not recognized')
    return filenum


def identity(item):
    """convenience function"""
    return item


def has_unaccent(cr):
    """ Test if the database has an unaccent function.

    The unaccent is supposed to be provided by the PostgreSQL unaccent contrib
    module but any similar function will be picked by OpenERP.

    """
    cr.execute("SELECT proname FROM pg_proc WHERE proname='unaccent'")
    return len(cr.fetchall()) > 0


def get_missing_links(header, df):
    """ Find missing links in a dataframe
    :param dict header:
    :param pd.Dataframe df:
    :return: a list of dict
    :rtype: list
    """
    links = []
    for transaction_id, transaction_df in df.groupby(['transaction_id']):
        for src in range(header['node_count']):
            for dst in range(header['node_count']):
                if src == dst:
                    continue
                if not ((transaction_df['src'] == src) & (transaction_df[
                    'dst'] == dst)).any():
                    links.append({'transaction_fist_date': transaction_df.
                        index[0], 'transaction_id': transaction_id, 'src':
                        src, 'dst': dst})
    return links


def sym_interval_around(x, xm, alpha=0.32):
    """
    In a distribution represented by a set of samples, find the interval that contains (1-alpha)/2 to each the left and
    right of xm. If xm is too marginal to allow both sides to contain (1-alpha)/2, add the remaining fraction to the
    other side.

    :param x: data values in the distribution
    :param xm: symmetric center value for which to find the interval
    :param alpha: fraction that will be outside of the interval (default 0.32, corresponds to 68 percent quantile)
    :return: interval (lower, upper) which contains 1-alpha symmetric around xm
    """
    assert (alpha > 0) & (alpha < 1)
    xt = x.copy()
    xt.sort()
    i = xt.searchsorted(xm)
    n = len(x)
    ns = int((1 - alpha) * n)
    i0 = i - ns / 2
    i1 = i + ns / 2
    if i0 < 0:
        i0 = 0
        i1 -= i0
    if i1 >= n:
        i1 = n - 1
        i0 -= i1 - n + 1
    return xt[int(i0)], xt[int(i1)]


def add_dest(df_conc, dest_labware, dest_start=1):
    """Setting destination locations for samples & primers.
    Adding to df_conc:
      [dest_labware, dest_location]
    """
    dest_start = int(dest_start)
    df_conc['dest_labware'] = dest_labware
    dest_end = dest_start + df_conc.shape[0]
    df_conc['dest_location'] = list(range(dest_start, dest_end))
    return df_conc


def _length(stream):
    """Returns the size of the given file stream."""
    original_offset = stream.tell()
    stream.seek(0, 2)
    size = stream.tell()
    stream.seek(original_offset)
    return size


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 read_pprog(filename):
    """
    Read a Bruker pulse program (pulseprogram) file.

    Resultsing dictionary contains the following keys:

    ========    ===========================================================
    key         description
    ========    ===========================================================
    var         dictionary of variables assigned in pulseprogram
    incr        list of lists containing increment times
    loop        list of loop multipliers
    phase       list of lists containing phase elements
    ph_extra    list of lists containing comments at the end of phase lines
    ========    ===========================================================

    The incr,phase and ph_extra lists match up with loop list.  For example
    incr[0],phase[0] and ph_extra[0] are all increment and phase commands
    with comments which occur during loop 0 which has loop[0] steps.

    Parameters
    ----------
    filename : str
        Filename of pulseprogram file to read from,

    Returns
    -------
    dic : dict
        A dictionary with keys described above.

    See Also
    --------
    write_pprog : Write a Bruker pulse program to file.

    """
    f = open(filename, 'r')
    var = dict()
    loop = []
    incr = [[]]
    phase = [[]]
    ph_extra = [[]]
    for line in f:
        if ';' in line:
            comment = line[line.index(';'):]
            text = line[:line.index(';')].strip()
        else:
            comment = ''
            text = line.strip()
        if len(text.split()) != 0:
            s = text.split()[0]
            if s.isdigit() or s[-1] == ',':
                text = text[len(s):].strip()
        if text == '' or text[0] == '#':
            continue
        if '"' in text:
            if '=' in line:
                text = text.strip('"')
                t = text.split('=')
                if len(t) >= 2:
                    key, value = t[0], t[1]
                    var[key] = value
                else:
                    pass
                continue
            else:
                continue
        if text[0:2] == 'lo':
            loop.append(text.split()[4])
            incr.append([])
            phase.append([])
            ph_extra.append([])
            continue
        tokens = text.split()
        if len(tokens) >= 2:
            token2 = tokens[1]
            if token2.startswith('id') or token2.startswith('dd'):
                incr[len(loop)].append(int(token2[2:]))
                continue
            if token2.startswith('ipu') or token2.startswith('dpu'):
                incr[len(loop)].append(int(token2[3:]))
                continue
            if token2.startswith('ip') or token2.startswith('dp'):
                phase[len(loop)].append(int(token2[2:]))
                last = text.find(' ', text.index('ip'))
                if last == -1:
                    ph_extra[len(loop)].append('')
                else:
                    ph_extra[len(loop)].append(text[last:].strip())
                continue
    f.close()
    incr.pop()
    phase.pop()
    ph_extra.pop()
    for i, t in enumerate(loop):
        if t.isdigit():
            loop[i] = int(t)
        elif t in var and var[t].isdigit():
            loop[i] = int(var[t])
    dic = {'var': var, 'incr': incr, 'loop': loop, 'phase': phase,
        'ph_extra': ph_extra}
    return dic


def show_length(files):
    """check length of list (without creating a new node)"""
    if not isinstance(files, (list, tuple)):
        raise ValueError('{} should be iterables'.format(files))
    print(len(files))
    return files


def _draw_heatmap(*args, **kwargs):
    """Wraps seaborn.heatmap to work with long, tidy format dataframes.
    """
    import seaborn as sb
    data = kwargs.pop('data')
    d = data.pivot(index=args[1], columns=args[0], values=args[2])
    return sb.heatmap(d, **kwargs)


def is_file_using_correct_storage(content):
    """Return true if the given content is a file and using the
        correct selected storage in the service.
        """


def get_direct_url(data: dict) ->str:
    """
    Returns the direct video URL from the returned data by YoutubeDL, like
    https://r1---sn-vg5obxn25po-cjod.googlevideo.com/videoplayback?...
    """
    return data['entries'][0]['url']


@staticmethod
def exp10(x):
    """Exp function (base 10) as a wrapper.

        Note that this is equivalent to natural-exp function as a wrapper.
        """
    return 10 ** x


def grayscale(image):
    """
    Convert the image to grayscale.

    :param image: The image to convert.
    :return: An image.
    """
    return image.convert('L')


def new_sequence(prev, curr):
    """Allow depth to be incremented if starting a new sequence"""
    return (curr.idx == 0 and curr.depth == prev.depth + 1 and curr.typ !=
        prev.typ)


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

        :param IntegrationConnectedType val:
        :rtype: EventType
        """
    return cls('integration_connected', val)


@classmethod
def detect(cls, old_config, new_config):
    """Detect if algorithm definition in `new_config` differs from `old_config`"""
    if old_config['algorithms'] != new_config['algorithms']:
        yield cls(old_config, new_config)


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

        :param DomainVerificationRemoveDomainType val:
        :rtype: EventType
        """
    return cls('domain_verification_remove_domain', val)


def sanitize_empty(value):
    """
    Built-in sanitizer which replaces the original value with empty string.
    """
    return None if value is None else ''


def merge_dicts(*dict_args):
    """
    Given any number of dicts, shallow copy and merge into a new dict,
    precedence goes to key value pairs in latter dicts.
    """
    result = {}
    for dictionary in dict_args:
        result.update(dictionary)
    return result


def return_vars(var_list):
    """Generate RETURN query with the input variables."""
    return 'RETURN {}\n'.format(', '.join(var_list))


@classmethod
def get_priority(cls) ->int:
    """
        Priority of this editor
        """
    return cls.on_get_priority()


def create_ref(*parts):
    """Create a reference from `parts`

    For example:

        assert create_ref("#", "definitions", "foo") == "#/definitions/foo"

    :param Tuple[str] parts:
    :rtype: str
    """
    return '/'.join(parts)


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

        :param UserResendEmailsResult val:
        :rtype: UserResendResult
        """
    return cls('success', val)


def prepare(prefix):
    """
    Prepare the environment.
    """
    return True


def get_supported_encodings():
    """
    Get a list of encodings supported by ``converter`` module.

    >>> get_supported_encodings()
    ['unicode', 'zawgyi', 'wininnwa']
    """
    return ['unicode', 'zawgyi', 'wininnwa']


@classmethod
def attendee2values(cls, attendee):
    """
        Convert a attendee object into values for create or write
        """
    res = {}
    if attendee.value.lower().startswith('mailto:'):
        res['email'] = attendee.value[7:]
    else:
        res['email'] = attendee.value
    res['status'] = ''
    if hasattr(attendee, 'partstat_param'):
        if attendee.partstat_param.lower() in dict(cls.status.selection):
            res['status'] = attendee.partstat_param.lower()
    res['attendee'] = attendee.serialize()
    return res


def print_change_report(potential_changes, required_changes, ucfg, logger=None
    ):
    """
    Pass in the list of changes generated by check_config file.
    print out in a pretty format the changes required

    Args:
        potential_changes: List of warnings about config property changes
                           especialy about defaults retruned from
                           :func:`~changes.ChangeLog.get_active_changes`.
        potential_required: List of critical changes returned from
                            :func:`~changes.ChangeLog.get_active_changes`.
    """
    msg = '{: <20} {: <25} {: <25} {: <25}'
    if logger != None:
        out = logger.info
    else:
        out = print
    msg_len = 90
    out(' ')
    out(' ')
    title = 'Configuration File Change Log Report:'
    out(title)
    header = '=' * len(title)
    out(header)
    if len(potential_changes) > 0:
        out("""Default changes - Warnings issued only when old default values are detected in file.
"""
            )
        out('Default Changes:')
        out('No. of default changes: {:0.0f}'.format(len(potential_changes)))
        any_warnings = True
        out(' ')
        out(msg.format(' Section', 'Item', 'Old Default', 'New Default'))
        out('-' * msg_len)
        for w in potential_changes:
            out(msg.format(w[0][0], w[0][1], w[0][3], w[1][3]))
        out(' ')
        out(' ')
    msg = '{: <50} {: <50}'
    if len(required_changes) > 0:
        any_errors = True
        out('Required Changes:')
        out('No. of necessary changes: {:0.0f}'.format(len(required_changes)))
        out(' ')
        out(msg.format('From', 'To'))
        out('-' * msg_len)
        for e in required_changes:
            orig = '{}/{}'.format(e[0][0], e[0][1])
            if e[1] != 'removed':
                to = '{}/{}'.format(e[1][0], e[1][1])
            else:
                to = e[1]
            out(msg.format(orig, to))
        out(' ')
        out(' ')
    if not any_errors and not any_warnings:
        out('No required changes or old defaults were reported with the config file.\n'
            )


def delimit(delimiters, content):
    """
    Surround `content` with the first and last characters of `delimiters`.

    >>> delimit('[]', "foo")  # doctest: +SKIP
    '[foo]'
    >>> delimit('""', "foo")  # doctest: +SKIP
    '"foo"'
    """
    if len(delimiters) != 2:
        raise ValueError('`delimiters` must be of length 2. Got %r' %
            delimiters)
    return ''.join([delimiters[0], content, delimiters[1]])


@staticmethod
def _url_is_wishlist(url):
    """
        Determine if the given string or URL matches a wishlist.

        :param url: URL or string to test
        :type url: str
        :return: whether url is a wishlist URL
        :rtype: bool
        """
    return url.startswith('https://www.amazon.com/gp/registry/wishlist/')


def strip_docstring(docstring):
    """Return contents of docstring."""
    docstring = docstring.strip()
    quote_types = ["'''", '"""', "'", '"']
    for quote in quote_types:
        if docstring.startswith(quote) and docstring.endswith(quote):
            return docstring.split(quote, 1)[1].rsplit(quote, 1)[0].strip()
    raise ValueError('We only handle strings that start with quotes')


def _bitvalues(bitnames):
    """ Compute the association between bit names and values.
      Return name->value and value>name mapping.
  """
    n = 1
    values = {}
    names = {}
    for name in bitnames:
        values[name] = n
        names[n] = name
        n <<= 1
    return names, values


def find_cycle(graph):
    """find a cycle in an undirected graph

    :param graph: undirected graph in listlist or listdict format
    :returns: list of vertices in a cycle or None
    :complexity: `O(|V|+|E|)`
    """
    n = len(graph)
    prec = [None] * n
    for u in range(n):
        if prec[u] is None:
            S = [u]
            prec[u] = u
            while S:
                u = S.pop()
                for v in graph[u]:
                    if v != prec[u]:
                        if prec[v] is not None:
                            cycle = [v, u]
                            while u != prec[v] and u != prec[u]:
                                u = prec[u]
                                cycle.append(u)
                            return cycle
                        else:
                            prec[v] = u
                            S.append(v)
    return None


def FF(Vmp, Imp, Voc, Isc):
    """Return FFv the fill factor of a solar cell.
    given Voc - open circuit voltage (volts)"""
    return Vmp * Imp / (Voc * Isc)