def get_current_exchange_rate(src_currency, tgt_currency):
    """
		Access Google's free currency exchange API.

		Use the 3-char ISO currency codes to specify source and target currency.

		Usage:
		Convert $15.43USD to Euros:
		amt_in_euros = 15.43 * get_current_exchange_rate('USD', 'EUR')
	"""
    import urllib2
    response = urllib2.urlopen(
        'http://www.google.com/ig/calculator?hl=en&q=1%s=?%s' % (
        src_currency, tgt_currency))
    data = response.read()
    print(data)
    index = data.find('rhs: "') + 6
    working_str = data[index:]
    print(working_str)
    exchange_rate, discard_remainder = working_str.split(' ', 1)
    print(exchange_rate)
    return float(exchange_rate)


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 played_card_dict(played_card):
    """
    Returns a dictionary containing:
    - the player who played the card
    - the played card
    :param played_card:
    :return:
    """
    return {'player_id': played_card.player.id, 'card': str(played_card.card)}


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 '+'


def find_field_in_modified_field(field):
    """
    Returns the field from a modified field argument (or just the field argument if it is not modified). A modified
    field represents either a wrapped dimension (e.g. DatetimeInterval) or a field wrapped in a filter.
    """
    modified_field = field
    while hasattr(modified_field, 'dimension'):
        modified_field = modified_field.dimension
    while hasattr(modified_field, 'field'):
        modified_field = modified_field.field
    return modified_field


@staticmethod
def get_extreme_points(for_contour=None):
    """
        Get the leftmost, topmost, etc points of a contour
        :param for_contour: a CV2 contour (e.g. returned from get_contours)
        :return: leftmost, rightmost, topmost, bottommost coordinates
        """
    if for_contour is None:
        return None, None, None, None
    leftmost = tuple(for_contour[for_contour[:, :, (0)].argmin()][0])
    rightmost = tuple(for_contour[for_contour[:, :, (0)].argmax()][0])
    topmost = tuple(for_contour[for_contour[:, :, (1)].argmin()][0])
    bottommost = tuple(for_contour[for_contour[:, :, (1)].argmax()][0])
    return leftmost, rightmost, topmost, bottommost


@classmethod
def get_client_info(cls, flow_name, metadata):
    """
        Environment may customize the information returned to the client about the environment

        Parameters
        ----------
        flow_name : str
            Name of the flow
        metadata : dict
            Metadata information regarding the task

        Returns
        -------
        str : Information printed and returned to the user
        """
    return 'Local environment'


def append(a, vancestors):
    """
    Append ``a`` to the list of the virtual ancestors, unless it is already
    included.
    """
    add = True
    for j, va in enumerate(vancestors):
        if issubclass(va, a):
            add = False
            break
        if issubclass(a, va):
            vancestors[j] = a
            add = False
    if add:
        vancestors.append(a)


def htmlquote(text):
    """
    Encodes `text` for raw use in HTML.

        >>> htmlquote(u"<'&\\">")
        u'<'&">'
    """
    text = text.replace(u'&', u'&')
    text = text.replace(u'<', u'<')
    text = text.replace(u'>', u'>')
    text = text.replace(u"'", u''')
    text = text.replace(u'"', u'"')
    return text


def surf_vol(length, girth):
    """Calculate the surface volume of an animal from its length and girth

    Args
    ----
    length: float or ndarray
        Length of animal (m)
    girth: float or ndarray
        Girth of animal (m)

    Returns
    -------
    surf:
        Surface area of animal (m^2)
    vol: float or ndarray
        Volume of animal (m^3)
    """
    import numpy
    a_r = 0.01 * girth / (2 * numpy.pi)
    stl_l = 0.01 * length
    c_r = stl_l / 2
    e = numpy.sqrt(1 - a_r ** 2 / c_r ** 2)
    surf = 2 * numpy.pi * a_r ** 2 + 2 * numpy.pi * (a_r * c_r / e
        ) * 1 / numpy.sin(e)
    vol = 4 / 3 * numpy.pi * a_r ** 2 * c_r
    return surf, vol


def listdir():
    """
        Returns iterable of filenames of directory contents.  Raises an
        exception if asset is not a directory.
        """


def parse_entry_point(ep):
    """Check and parse a 'package.module:func' style entry point specification.

    Returns (modulename, funcname)
    """
    if ':' not in ep:
        raise ValueError("Invalid entry point (no ':'): %r" % ep)
    mod, func = ep.split(':')
    if not func.isidentifier():
        raise ValueError('Invalid entry point: %r is not an identifier' % func)
    for piece in mod.split('.'):
        if not piece.isidentifier():
            raise ValueError('Invalid entry point: %r is not a module path' %
                piece)
    return mod, func


def _shout(text):
    """
    Describe here what this function does,
    its input parameters, and what it returns.

    In this example the function converts the input string
    to uppercase.
    """
    result = text.upper()
    return result


@staticmethod
def getCreateTable():
    """Create the table to hold the hostnames"""
    return 'create table host (host char(256))'


def theta_Cs_Na_PK74(T, P):
    """c-c': caesium sodium [PK74]."""
    theta = -0.033
    valid = T == 298.15
    return theta, valid


def humanize_bitrate(bitrate):
    """Humanize bitrate from integer."""
    if bitrate is not None:
        for divisor, symbol in [(1000 ** 1, 'Kbps'), (1, 'bps')]:
            if bitrate >= divisor:
                break
        return f'{round(bitrate / divisor)} {symbol}'


@classmethod
def from_range(cls, low, high):
    """
        Construct an intspanlist from the low value to the high value,
        inclusive. I.e., closed range, not the more typical Python
        half-open range.

        :param int low: Low bound.
        :param int high: High bound.
        :returns: New intspanlist low-high.
        :rtype: intspanlist
        """
    return cls(range(low, high + 1))


def get_journal_filename(response):
    """Extracts the name of the file submitted from the response"""
    return response['attachments'][0]['filename']


def get_pull_requests_by_head(repo, branch_name):
    """Returns all pull requests with head equal to the given branch name."""
    if branch_name == repo.default_branch:
        return None
    return list(repo.pull_requests(head=repo.owner.login + ':' + branch_name))


def _start_string(num_total, tag, num_processes):
    """Returns a string to be displayed before processing begins. It contains
    the number of total items and the number of processes. It is prefixed by the `tag`.

    Where information are not available or a division by zero would occur, a `-` or `0ms` is returned for
    that field.
    """
    fmt_string = (
        '{tag}: Start processing {num_total} items with {num_processes} processes...'
        )
    return fmt_string.format(tag=tag, num_total=num_total, num_processes=
        num_processes)


def concat_columns(df, sep='|'):
    """Concatenate all columns of a dataframe into a pd.Series
    """
    for i in range(df.shape[1]):
        vec = df.iloc[:, (i)].astype(str)
        if i == 0:
            column = vec
        else:
            column = column.str.cat(vec, sep=sep)
    return column


def validate_format(*_arg):
    """
    This method can be overridden with format validation logic.
    """
    return True


def triple_rep(w):
    """Return 'True' if w has a letter repeated 3 times consecutively."""
    for i in range(len(w) - 2):
        if w[i] == w[i + 1] and w[i] == w[i + 2] and w[i].isalpha():
            return True
    return False


def build_csv(data, filename='gyuto_data.csv'):
    """Create basic CSV file from data

    "data" should be a dict-type, output lines are of the form "{key},{value}"
    """
    with open(filename, 'w') as fout:
        fout.write('name,value\n')
        for k, v in data.iteritems():
            fout.write('{0},{1}\n'.format(k, v))


def region(xds, name='REGION1', ra=None, dec=None, pixels=None, pol=-1,
    channels=-1):
    """
    Create a new region Data variable in the Dataset

    .. note:: This function currently only supports rectangles and integer pixel boundaries

    Parameters
    ----------
    xds : xarray.core.dataset.Dataset
        input image dataset
    name : str
        dataset variable name for region, overwrites if already present
    ra : list
        right ascension coordinate range in the form of [min, max]. Default None means all
    dec : list
        declination coordinate range in the form of [min, max]. Default None means all
    pixels : array_like
        array of shape (N,2) containing pixel box. OR'd with ra/dec
    pol : int or list
        polarization dimension(s) to include in region.  Default of -1 means all
    channels : int or list
        channel dimension(s) to include in region.  Default of -1 means all

    Returns
    -------
    xarray.core.dataset.Dataset
        New Dataset
    """
    import numpy as np
    import xarray as xr
    if not name.strip():
        name = 'REGIONX'
    if ra is None:
        ra = [0.0, 0.0]
    if dec is None:
        dec = [0.0, 0.0]
    if pixels is None:
        pixels = np.zeros((1, 2), dtype=int) - 1
    pixels = np.array(pixels, dtype=int)
    if pixels.ndim != 2 or pixels.shape[1] != 2:
        print('ERROR: pixels parameter not a (N,2) array')
        return None
    pol = np.array(np.atleast_1d(pol), dtype=int)
    if pol[0] == -1:
        pol = list(range(len(xds['pol'])))
    channels = np.array(np.atleast_1d(channels), dtype=int)
    if channels[0] == -1:
        channels = list(range(len(xds['chan'])))
    region = xr.ones_like(xds.IMAGE, dtype=bool).where((xds.right_ascension >
        np.min(ra)) & (xds.right_ascension < np.max(ra)) & (xds.declination >
        np.min(dec)) & (xds.declination < np.max(dec)), False)
    region = region | xr.ones_like(xds.IMAGE, dtype=bool).where((xds.d0 >
        np.min(pixels[:, (0)])) & (xds.d0 < np.max(pixels[:, (0)])) & (xds.
        d1 > np.min(pixels[:, (1)])) & (xds.d1 < np.max(pixels[:, (1)])), False
        )
    region = region.where(xds.pol.isin(xds.pol[pol]), False)
    region = region.where(xds.chan.isin(xds.chan[channels]), False)
    xds = xds.assign(dict([(name, region)]))
    return xds


def page_width_or_height(box, containing_block_size):
    """Take a :class:`OrientedBox` object and set either width, margin-left
    and margin-right; or height, margin-top and margin-bottom.

    "The width and horizontal margins of the page box are then calculated
     exactly as for a non-replaced block element in normal flow. The height
     and vertical margins of the page box are calculated analogously (instead
     of using the block height formulas). In both cases if the values are
     over-constrained, instead of ignoring any margins, the containing block
     is resized to coincide with the margin edges of the page box."

    http://dev.w3.org/csswg/css3-page/#page-box-page-rule
    http://www.w3.org/TR/CSS21/visudet.html#blockwidth

    """
    remaining = containing_block_size - box.padding_plus_border
    if box.inner == 'auto':
        if box.margin_a == 'auto':
            box.margin_a = 0
        if box.margin_b == 'auto':
            box.margin_b = 0
        box.inner = remaining - box.margin_a - box.margin_b
    elif box.margin_a == box.margin_b == 'auto':
        box.margin_a = box.margin_b = (remaining - box.inner) / 2
    elif box.margin_a == 'auto':
        box.margin_a = remaining - box.inner - box.margin_b
    elif box.margin_b == 'auto':
        box.margin_b = remaining - box.inner - box.margin_a
    box.restore_box_attributes()


def fi(df, close='close', volume='volume', col='fi', n=2):
    """Force Index (FI)
    It illustrates how strong the actual buying or selling pressure is. High
    positive values mean there is a strong rising trend, and low values signify
    a strong downward trend.
    http://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:force_index
    Args:
        close(pandas.Series): dataset 'Close' column.
        volume(pandas.Series): dataset 'Volume' column.
        n(int): n period.
        fillna(bool): if True, fill nan values.
    Returns:
        pandas.Series: New feature generated.
    """
    df = df.copy()
    fi = df[close].diff(n) * df[volume].diff(n)
    df[col] = fi
    return df


def render_pep440_bare(pieces):
    """TAG[.DISTANCE[.dev0]] .

    The ".dev0" means dirty.

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


def canonical_name(k, config):
    """Return the canonical name for a key.

    Handles user choice of '-' or '_' conventions by standardizing on whichever
    version was set first. If a key already exists in either hyphen or
    underscore form, the existing version is the canonical name. If neither
    version exists the original key is used as is.
    """
    try:
        if k in config:
            return k
    except TypeError:
        return k
    altk = k.replace('_', '-') if '_' in k else k.replace('-', '_')
    if altk in config:
        return altk
    return k


def _do_get_featuregroup_features_list(featuregroup, version,
    featurestore_metadata):
    """
    Gets a list of all names of features in a featuregroup in a featurestore.

    Args:
        :featuregroup: Featuregroup name.
        :version: Version of the featuregroup to use.
        :featurestore_metadata: Metadata of the featurestore.

    Returns:
        A list of names of the features in this featuregroup.
    """
    featuregroup_version = featuregroup + '_' + str(version)
    features = featurestore_metadata.featuregroups[featuregroup_version
        ].features
    return list(map(lambda f: f.name, features))


@classmethod
def _get_enabled_storage_plugin_names(cls, config):
    """
        Get the names of storage plugins that are enabled in the configuration.
        """
    return set(config.get_config('storage', 'plugins', b'').decode('ascii')
        .split(u',')) - {u''}


@staticmethod
def showSub(class_, field):
    """Show button "details" only under field "title", if a method named
           "getSubTitle" is defined on p_class_."""
    return field.name == 'title' and class_.hasSubTitle()


def explain_not_fair(fsm, state):
    """
    Explain why state of fsm is not a fair state.

    fsm -- the fsm;
    state -- a non-fair state of fsm.
    """
    pass


def str2lst(astr_input, astr_separator=' '):
    """
  Breaks a string at <astr_separator> and joins into a
  list. Steps along all list elements and strips white
  space.

  The list elements are explicitly ascii encoded.
  """
    alistI = astr_input.split(astr_separator)
    alistJ = []
    for i in range(0, len(alistI)):
        alistI[i] = alistI[i].strip()
        alistI[i] = alistI[i].encode('ascii')
        if len(alistI[i]):
            alistJ.append(alistI[i])
    return alistJ


def compressedListTransform(compressedList, timeStep, timeThreshold=None):
    """
    Isolates the unique values in compressedList and converts them to time

    timeThreshold can be set to ignore values that are not long enough, adding
    their content to whatever came before (prevents fragmenting data too much).
    """
    returnDict = {}
    countDict = {}
    lastGoodLabel = None
    for label, start, end in compressedList:
        countDict.setdefault(label, 0)
        returnDict.setdefault(label, [])
        startTime = start * timeStep
        endTime = end * timeStep
        tmpDuration = (end - start) * timeStep
        if timeThreshold is not None and tmpDuration < timeThreshold:
            if lastGoodLabel is not None and returnDict[lastGoodLabel] != []:
                returnDict[lastGoodLabel][-1][1] = endTime
                continue
        if label == lastGoodLabel:
            returnDict[label][-1][1] = endTime
        else:
            returnDict[label].append([startTime, endTime, str(countDict[
                label])])
            countDict[label] += 1
            lastGoodLabel = label
    return returnDict


def vertex_group_deselect():
    """Deselect all selected vertices assigned to the active vertex group

    """
    pass


def noneFunc(thing):
    """
    Function checks if nothing is supplied

    Returns True if nothing it is "null"
    Returns False if it haves stuff
    """
    if thing == None or thing == ' ' or thing == '':
        return True
    else:
        return False


def is_iterable(obj):
    """
    Are we being asked to look up a list of things, instead of a single thing?
    We check for the `__iter__` attribute so that this can cover types that
    don't have to be known by this module, such as NumPy arrays.

    Strings, however, should be considered as atomic values to look up, not
    iterables. The same goes for tuples, since they are immutable and therefore
    valid entries.

    We don't need to check for the Python 2 `unicode` type, because it doesn't
    have an `__iter__` attribute anyway.
    """
    return hasattr(obj, '__iter__') and not isinstance(obj, str
        ) and not isinstance(obj, tuple)


@staticmethod
def prepareKeyValue(outputData, key, value):
    """ This function will append key and value in
            outputData
            if variable key exist, it will rename by
                    key1, key2, ...
        """
    for i in range(1, 100):
        var = '$(' + key + str(i) + ')'
        if not outputData.get(var, None):
            outputData[var] = value
            return var
        else:
            continue


def uri_for_service(region, service_id):
    """
        Get the URI for the service in a given region.

        :param six.text_type service_id: the ID of the service.
        :returns: URL to use if it exists, otherwise an empty string
        :rtype: six.text_type
        """


def Bejan_L(dP, L, mu, alpha):
    """Calculates Bejan number of a length or `Be_L` for a fluid with the
    given parameters flowing over a characteristic length `L` and experiencing
    a pressure drop `dP`.

    .. math::
        Be_L = \\frac{\\Delta P L^2}{\\mu \\alpha}

    Parameters
    ----------
    dP : float
        Pressure drop, [Pa]
    L : float
        Characteristic length, [m]
    mu : float, optional
        Dynamic viscosity, [Pa*s]
    alpha : float
        Thermal diffusivity, [m^2/s]

    Returns
    -------
    Be_L : float
        Bejan number with respect to length []

    Notes
    -----
    Termed a dimensionless number by someone in 1988.

    Examples
    --------
    >>> Bejan_L(1E4, 1, 1E-3, 1E-6)
    10000000000000.0

    References
    ----------
    .. [1] Awad, M. M. "The Science and the History of the Two Bejan Numbers."
       International Journal of Heat and Mass Transfer 94 (March 2016): 101-3.
       doi:10.1016/j.ijheatmasstransfer.2015.11.073.
    .. [2] Bejan, Adrian. Convection Heat Transfer. 4E. Hoboken, New Jersey:
       Wiley, 2013.
    """
    return dP * L ** 2 / (alpha * mu)


def _format_result(criterion_name, success, detail):
    """Returns the SLA result dict corresponding to the current state."""
    return {'criterion': criterion_name, 'success': success, 'detail': detail}


def sanitizer(unit):
    """
    Sanitizes a unit string. That is, it is de-blanked, and mu and µ symbols
    are changed to u for micro.

    :param unit: The unit that needs to be sanitized.

    :returns: the sanitized unit.
    :rtype: str
    """
    micro = 'µ'
    mugr = 'μ'
    return unit.replace(' ', '').replace('mu', 'u').replace(micro, 'u'
        ).replace(mugr, 'u')


@classmethod
def clear(cls, val, flags):
    """
        Clear all bits in flags inside val, return new val
        :param val: int
        :type val: int
        :param flags: int
        :type flags: int
        :return int
        :rtype int
        """
    return val & ~flags


def types():
    """Get supported layer network types for network building."""
    return {'autoencoder': 'Autoencoder', 'factor': 'Factor Graph',
        'multilayer': 'Feedforward MultiLayer Network', 'shallow':
        'Shallow Network'}


def is_variable(x):
    """
    Checks if the provided expression x is a variable, i.e., a string that
    starts with ?.

    >>> is_variable('?x')
    True
    >>> is_variable('x')
    False
    """
    return isinstance(x, str) and len(x) > 0 and x[0] == '?'


def reports_to_textblock():
    """Write the reports

    """
    pass


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 mpiprint(string='', end='\n', rank=0, comm=None):
    """print the given string to the given rank"""
    from pyina.mpi import world
    if comm is None:
        comm = world
    if not hasattr(rank, '__len__'):
        rank = rank,
    if comm.rank in rank:
        print((string + end,))


def offset(image, xoffset, yoffset=None):
    """Returns a copy of the image where data has been offset by the given
    distances. Data wraps around the edges. If **yoffset** is omitted, it
    is assumed to be equal to **xoffset**.

    :param xoffset: The horizontal distance.
    :param yoffset: The vertical distance.  If omitted, both
        distances are set to the same value.
    :rtype: :py:class:`~PIL.Image.Image`
    """
    if yoffset is None:
        yoffset = xoffset
    image.load()
    return image._new(image.im.offset(xoffset, yoffset))


def new_target():
    """Add a new particle target

    """
    pass


@classmethod
def clear(cls):
    """ Remove every task from storage

		:return: None
		"""
    cls.registry_storage().clear()


@classmethod
def from_dict(cls, file_dict):
    """
        Create a SimpleUploadedFile object from a dictionary with keys:
           - filename
           - content-type
           - content
        """
    return cls(file_dict['filename'], file_dict['content'], file_dict.get(
        'content-type', 'text/plain'))


def generate(fieldname):
    """Generates a set of thumbnails from the given scales.
        """


def to_filename(name):
    """Convert a project or version name to its filename-escaped form

    Any '-' characters are currently replaced with '_'.
    """
    return name.replace('-', '_')


def mode():
    """mode of all incoming things"""


def check_dtype(ndarray):
    """Check that array is a standard dtype."""
    return ndarray.dtype.isbuiltin == 1 and not ndarray.dtype.hasobject


def _swap_dimensions(ds, geometry, drop_old=True):
    """Replace logical coordinates with physical ones. Does not work for llc.
    """
    keep_attrs = ['axis', 'c_grid_axis_shift']
    ds = ds.reset_coords()
    if geometry.lower() in ('llc', 'curvilinear'):
        raise ValueError("Can't swap dimensions if `geometry` is `llc`")
    for orig_dim in ds.dims:
        if 'swap_dim' in ds[orig_dim].attrs:
            new_dim = ds[orig_dim].attrs['swap_dim']
            coord_var = ds[new_dim]
            for coord_dim in coord_var.dims:
                if coord_dim != orig_dim:
                    coord_var = coord_var.isel(**{coord_dim: 0}).drop(coord_dim
                        )
            ds[new_dim] = coord_var
            for key in keep_attrs:
                if key in ds[orig_dim].attrs:
                    ds[new_dim].attrs[key] = ds[orig_dim].attrs[key]
    for orig_dim in ds.dims:
        if 'swap_dim' in ds[orig_dim].attrs:
            new_dim = ds[orig_dim].attrs['swap_dim']
            ds = ds.swap_dims({orig_dim: new_dim})
            if drop_old:
                ds = ds.drop(orig_dim)
    return ds


def list_templates():
    """
        List the available templates.

        :returns: an iterable of the endpoint templates
        :rtype: iterable
        """


def format_label_value(*values):
    """
    Construct a label value.

    If multiple value components are provided, they are joined by underscores.
    """
    return '_'.join(values)


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


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


@staticmethod
def _is_satisfied(what) ->bool:
    """
            Is collection conditions satisfied

            :param what: collection to check
            :return: True if satisfied else False
        """
    return len(what) != 0


def asciiupper(s):
    """convert a string to uppercase if ASCII

    Raises UnicodeDecodeError if non-ASCII characters are found."""
    s.decode('ascii')
    return s.upper()


def get_version_from_setup_file():
    """parse version info from setup.py file.

    Return the version string.

    """
    with open('setup.py', 'r') as f:
        for line in f:
            if 'version=' in line:
                return line.split('=')[1][1:-3]
    raise Exception('version not found in setup.py')


def get_local_group_information(task, portgroup):
    """Extract the portgroup information.

    The information is returned in the form of::

        {
            'id': portgroup.uuid,
            'name': portgroup.name,
            'bond_mode': portgroup.mode,
            'bond_properties': {
                'bond_propertyA': 'valueA',
                'bond_propertyB': 'valueB',
            }
        }

    :param task: a task containing the Node object.
    :param portgroup: Ironic portgroup object to extract data for.
    :returns: port group information as a dict
    """
    portgroup_properties = {}
    for prop, value in portgroup.properties.items():
        key = prop if prop.startswith('bond') else 'bond_%s' % prop
        portgroup_properties[key] = value
    return {'id': portgroup.uuid, 'name': portgroup.name, 'bond_mode':
        portgroup.mode, 'bond_properties': portgroup_properties}


@staticmethod
def is_task():
    """ Determines if the object is a Task. """
    return False


def rqst_user_input(property, current_value):
    """
    Ask user to modify the property
    given and retuns the modification
    """
    print('Currently: {prop} = {value}'.format(prop=property, value=
        current_value))
    update = input('Please modify (leave blank to leave unchanged):')
    if update == '':
        return current_value
    else:
        return update


def colors_reverse():
    """Flip direction of vertex colors inside faces

    """
    pass


def socket_set_hwm(socket, hwm=-1):
    """libzmq 2/3/4 compatible sethwm"""
    try:
        socket.sndhwm = socket.rcvhwm = hwm
    except AttributeError:
        socket.hwm = hwm


def session_hist_tex(f, session_data, threshold_requests_per_session):
    """
    Write session caracteristics in latex file

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

        session_data: pandas dataframe of sessions

        threshold_requests_per_session: int for filter number of requests per session

    Returns
    -------
        File (Optionnal)
    """
    digit_dic = {'0': 'Zero', '1': 'One', '2': 'Two', '3': 'Three', '4':
        'Four', '5': 'Five', '6': 'Six', '7': 'Seven', '8': 'Eight', '9':
        'Nine'}
    f.write('\n% 2. Histogram sessions with given requests')
    f.write('\n\\newcommand{\\%s}{%d}' % ('ThresholdRequestsPerSession',
        threshold_requests_per_session))
    for reqs in range(1, threshold_requests_per_session + 1):
        num_of_sessions = session_data[session_data.requests == reqs].shape[0]
        pc_of_sessions = 100.0 * num_of_sessions / session_data.shape[0]
        f.write('\n\\newcommand{\\%s}{%d}' % ('SessionsWith%sRequests' %
            digit_dic[str(reqs)], num_of_sessions))
        f.write('\n\\newcommand{\\%s}{%.1f}' % ('PCSessionsWith%sRequests' %
            digit_dic[str(reqs)], pc_of_sessions))
    num_of_sessions = session_data[session_data.requests >
        threshold_requests_per_session].shape[0]
    pc_of_sessions = 100.0 * num_of_sessions / session_data.shape[0]
    f.write('\n\\newcommand{\\%s}{%d}' % ('SessionsWithMT%sRequests' %
        digit_dic[str(threshold_requests_per_session)], num_of_sessions))
    f.write('\n\\newcommand{\\%s}{%.1f}' % ('PCSessionsWithMT%sRequests' %
        digit_dic[str(threshold_requests_per_session)], pc_of_sessions))
    return f


def display(zelf, obj=None, keys=[], txt='', direct=''):
    """ display the content of an objectect. """
    from madz.getters import days
    res = ''
    if not obj:
        obj = zelf
    if not keys:
        keys = obj.keys()
    for key in keys:
        val = getattr(obj, key, None)
        if val:
            res += ' ' + str(val).strip()
    d = days(obj)
    res += ' - %s' % d
    if txt:
        res = '%s %s' % (txt, res.strip())
    if direct:
        zelf.direct(res.strip())
    else:
        zelf.reply(res.strip())


def select_non_manifold(extend: bool=True, use_wire: bool=True,
    use_boundary: bool=True, use_multi_face: bool=True, use_non_contiguous:
    bool=True, use_verts: bool=True):
    """Select all non-manifold vertices or edges

    :param extend: Extend, Extend the selection
    :type extend: bool
    :param use_wire: Wire, Wire edges
    :type use_wire: bool
    :param use_boundary: Boundaries, Boundary edges
    :type use_boundary: bool
    :param use_multi_face: Multiple Faces, Edges shared by 3+ faces
    :type use_multi_face: bool
    :param use_non_contiguous: Non Contiguous, Edges between faces pointing in alternate directions
    :type use_non_contiguous: bool
    :param use_verts: Vertices, Vertices connecting multiple face regions
    :type use_verts: bool
    """
    pass


def do_intersect(bb1, bb2):
    """
    Helper function that returns True if two bounding boxes overlap.
    """
    if bb1[0] + bb1[2] < bb2[0] or bb2[0] + bb2[2] < bb1[0]:
        return False
    if bb1[1] + bb1[3] < bb2[1] or bb2[1] + bb2[3] < bb1[1]:
        return False
    return True


@classmethod
def via_tcpip_socket(cls, hostname, port, name=None, **kwargs):
    """Return a Driver with an underlying TCP Socket resource.

        Parameters
        ----------
        hostname :
            The ip address or hostname of the instrument.
        port :
            the port of the instrument.
        name :
            Unique name given within Lantz to the instrument for logging purposes.
            Defaults to one generated based on the class name if not provided.
        kwargs :
            keyword arguments passed to the Resource constructor on initialize.
        **kwargs :

        Returns
        -------

        """
    resource_name = 'TCPIP::%s::%s::SOCKET' % (hostname, port)
    return cls(resource_name, name, **kwargs)


def purchase_reserved_cache_nodes_offering(ReservedCacheNodesOfferingId=
    None, ReservedCacheNodeId=None, CacheNodeCount=None):
    """
    Allows you to purchase a reserved cache node offering.
    See also: AWS API Documentation

    :example: response = client.purchase_reserved_cache_nodes_offering(
        ReservedCacheNodesOfferingId='string',
        ReservedCacheNodeId='string',
        CacheNodeCount=123
    )

    :type ReservedCacheNodesOfferingId: string
    :param ReservedCacheNodesOfferingId: [REQUIRED]
            The ID of the reserved cache node offering to purchase.
            Example: 438012d3-4052-4cc7-b2e3-8d3372e0e706

    :type ReservedCacheNodeId: string
    :param ReservedCacheNodeId: A customer-specified identifier to track this reservation.
            Note
            The Reserved Cache Node ID is an unique customer-specified identifier to track this reservation. If this parameter is not specified, ElastiCache automatically generates an identifier for the reservation.
            Example: myreservationID

    :type CacheNodeCount: integer
    :param CacheNodeCount: The number of cache node instances to reserve.
            Default: 1

    :rtype: dict
    :return: {
        'ReservedCacheNode': {
            'ReservedCacheNodeId': 'string',
            'ReservedCacheNodesOfferingId': 'string',
            'CacheNodeType': 'string',
            'StartTime': datetime(2015, 1, 1),
            'Duration': 123,
            'FixedPrice': 123.0,
            'UsagePrice': 123.0,
            'CacheNodeCount': 123,
            'ProductDescription': 'string',
            'OfferingType': 'string',
            'State': 'string',
            'RecurringCharges': [
                {
                    'RecurringChargeAmount': 123.0,
                    'RecurringChargeFrequency': 'string'
                },
            ]
        }
    }

    :returns:
    General purpose:
    Current generation: cache.t2.micro , cache.t2.small , cache.t2.medium , cache.m3.medium , cache.m3.large , cache.m3.xlarge , cache.m3.2xlarge , cache.m4.large , cache.m4.xlarge , cache.m4.2xlarge , cache.m4.4xlarge , cache.m4.10xlarge
    Previous generation: cache.t1.micro , cache.m1.small , cache.m1.medium , cache.m1.large , cache.m1.xlarge

    Compute optimized: cache.c1.xlarge
    Memory optimized:
    Current generation: cache.r3.large , cache.r3.xlarge , cache.r3.2xlarge , cache.r3.4xlarge , cache.r3.8xlarge
    Previous generation: cache.m2.xlarge , cache.m2.2xlarge , cache.m2.4xlarge

    """
    pass


def allcap_differential(words):
    """
    Check whether just some words in the input are ALL CAPS
    :param list words: The words to inspect
    :returns: `True` if some but not all items in `words` are ALL CAPS
    """
    is_different = False
    allcap_words = 0
    for word in words:
        if word.isupper():
            allcap_words += 1
    cap_differential = len(words) - allcap_words
    if 0 < cap_differential < len(words):
        is_different = True
    return is_different


@classmethod
def is_group(cls, obj) ->bool:
    """
        Check chat is group

        :param obj:
        :return:
        """
    return cls._check(obj, [cls.GROUP])


def CRSI(df1, df2, n, price1='Close', price2='Close'):
    """
    Comparative Relative Strength Index
    Returns: list of floats = jhta.CRSI(df1, df2, n, price1='Close', price2='Close')
    Source: https://www.fmlabs.com/reference/default.htm?url=RSIC.htm
    """
    crsi_list = []
    for i in range(len(df1[price1])):
        if i + 1 < n:
            crsi = float('NaN')
        else:
            crsi = (df1[price1][i] / df2[price2][i] - df1[price1][i - n] /
                df2[price2][i - n]) / (df1[price1][i - n] / df2[price2][i - n])
        crsi_list.append(crsi)
    return crsi_list


def hz2bark(frq):
    """Convert hz to bark scale.

    Parameters
    ----------
    frq : np.ndarray, float
        frequency

    Returns
    -------
    bark : np.ndarray, float
        bark value
    """
    return 26.81 * frq / (1960 + frq) - 0.51


def receive(type=None):
    """Return all messages of the given type relevant to the current
        request.
        """


@staticmethod
def is_zero(st_vec):
    """
        Returns True iff an object of this class is None or its parameter
        'arr' is None

        Parameters
        ----------
        st_vec : StateVec|None

        Returns
        -------
        bool

        """
    return st_vec is None or st_vec.arr is None


def motor_lid_self_test():
    """
  This is the non-regression test of motor_lid.
  Look at the Tk window to check errors.
  """
    r_tests = [['simplest test',
        '--holder_diameter 160.0 --clearance_diameter 140.0 --central_diameter 80.0 --axle_hole_diameter 22.0'
        ], ['small side',
        '--holder_diameter 140.0 --clearance_diameter 110.0 --central_diameter 30.0 --axle_hole_diameter 6.0  --holder_crenel_number 7 --axle_B_distance 75.0 --axle_C_distance 30.0 --axle_C_hole_diameter 10.0 --axle_B_diameter 3.0 --axle_B_place small --axle_B_angle 0.2 --axle_C_angle -0.4 --motor_screw1_diameter 2.0 --motor_screw1_x_length 10.0 --fastening_BC_hole_diameter 2.0 --fastening_BC_bottom_position_diameter 50.0 --fastening_BC_bottom_angle 0.5 --fastening_BC_top_position_diameter 45.0 --fastening_BC_top_angle 0.3 --smoothing_radius 3.0 --axle_B_external_diameter 15.0'
        ], ['large side with side leg',
        '--holder_diameter 140.0 --clearance_diameter 110.0 --central_diameter 30.0 --axle_hole_diameter 6.0  --holder_crenel_number 7 --axle_B_distance 75.0 --axle_C_distance 30.0 --axle_C_hole_diameter 10.0 --axle_B_diameter 3.0 --axle_B_place large --axle_B_angle 0.2 --axle_C_angle -0.4 --motor_screw1_diameter 2.0 --motor_screw1_x_length 10.0 --fastening_BC_hole_diameter 2.0 --fastening_BC_bottom_position_diameter 50.0 --fastening_BC_bottom_angle 0.5 --fastening_BC_top_position_diameter 45.0 --fastening_BC_top_angle 0.3 --smoothing_radius 3.0 --axle_B_external_diameter 15.0 --leg_type side --leg_length 120.0 --foot_length 20.0 --leg_hole_diameter 10.0 --leg_hole_distance 60.0 --leg_hole_length 20.0 --leg_shift_length 30.0'
        ], ['seven holder',
        '--holder_diameter 59.0 --holder_hole_position_radius 30.5 --holder_hole_diameter 4.1 --holder_double_hole_diameter 0.9 --holder_double_hole_length 8.0 --holder_double_hole_position -1.0 --holder_double_hole_mark_nb 1 --holder_crenel_position 3.5 --holder_crenel_height 0.5 --holder_crenel_width 6.0 --holder_crenel_skin_width 3.0 --holder_crenel_router_bit_radius 0.1 --holder_smoothing_radius 0.0 --clearance_diameter 53.0 --axle_hole_diameter 5.9 --central_diameter 30.0 --axle_B_distance 0.0 --axle_B_external_diameter 0.0 --axle_C_distance 53.0 --axle_C_external_diameter 38.0 --holder_crenel_number 7'
        ], ['output files',
        '--holder_diameter 140.0 --clearance_diameter 110.0 --central_diameter 30.0 --axle_hole_diameter 6.0  --holder_crenel_number 7 --axle_B_distance 75.0 --axle_C_distance 30.0 --axle_C_hole_diameter 10.0 --axle_B_diameter 3.0 --axle_B_place large --axle_B_angle 0.2 --axle_C_angle -0.4 --motor_screw1_diameter 2.0 --motor_screw1_x_length 10.0 --fastening_BC_hole_diameter 2.0 --fastening_BC_bottom_position_diameter 50.0 --fastening_BC_bottom_angle 0.5 --fastening_BC_top_position_diameter 45.0 --fastening_BC_top_angle 0.3 --smoothing_radius 3.0 --axle_B_external_diameter 15.0 --leg_type side --leg_length 120.0 --foot_length 20.0 --leg_hole_diameter 10.0 --leg_hole_distance 60.0 --leg_hole_length 20.0 --leg_shift_length 30.0 --axle_B_hole_diameter 20.0 --output_file_basename test_output/motor_lid_self_test.dxf'
        ], ['last test',
        '--holder_diameter 100.0 --clearance_diameter 80.0 --central_diameter 30.0 --axle_hole_diameter 22.0  --holder_crenel_number 6 --axle_B_distance 52.0 --axle_C_distance 40.0 --axle_C_hole_diameter 10.0 --axle_B_diameter 3.0'
        ]]
    return r_tests


def remove_constant_columns(x_train, x_test=None):
    """
    Removes columns that have only one unique value.

    Parameters
    ----------
    x_train: Dataframe or array like - 2d
        Dataset

    x_test: Dataframe or array like - 2d
        Testing dataset, by default None.

    Returns
    -------
    Dataframe, *Dataframe
        Transformed dataframe with rows with a missing values in a specific row are missing

    Returns 2 Dataframes if x_test is provided.
    """
    keep_columns = list(filter(lambda x: x_train.nunique()[x] not in [0, 1],
        x_train.columns))
    x_train = x_train[keep_columns]
    if x_test is not None:
        x_test = x_test[keep_columns]
    return x_train, x_test


def normalise_between_2_values(arraylike, min_value, max_value, invert=False):
    """Normalises an array of data between two desired values.

    Any values below min_value will be converted to 0.
    Any values above max_value will be converted to 1.
    Optionally, the normalised array can be inverted, so that the original highest
    values are 0, and the original lowest values are now 1.

    Parameters
    ----------
    arraylike : np.ndarray
        Arraylike original data (numpy array or pandas Series)
    min_value : float
        Desired minimum value for normalisation
    max_value : float
        Desired max value for normalisation
    invert : bool
        If True, normalised data will be inverted (former highest value = 0)

    Returns
    -------
    normalised : np.ndarray
        Normalised array of data

    Usage
    -----
    from eccpy.tools import normalise_between_2_values
    # for array
    orig_array = np.array(range(0, 15))
    norm_array = normalise_between_2_values(orig_array, 3, 10)
    # for pandas Dataframe
    df["norm_data"] = normalise_between_2_values(df["orig_data"], 3, 10)
    """
    normalised = (arraylike - min_value) / (max_value - min_value)
    normalised[normalised > 1] = 1
    normalised[normalised < 0] = 0
    if invert:
        normalised = abs(normalised - 1)
    return normalised


@staticmethod
def _set_raw(target, raw_value, bitarray):
    """ put value into bit array """
    offset = int(target['offset'])
    size = int(target['size'])
    for digit in range(size):
        bitarray[offset + digit] = raw_value >> size - digit - 1 & 1 != 0
    return bitarray


def b_line(x0, y0, xf, yf):
    """ Implementation of Bresenham Algorithm - Adapted from Java
    Based on algorithm listed here: http://tech-algorithm.com/articles/drawing-line-using-bresenham-algorithm/
    Translated to Python by Maxwell Grady

    :param x0: int initial x location of line segment
    :param y0: int initial y location of line segment
    :param xf: int final x location of line segment
    :param yf: int final y location of line segment
    :returns:  list of points in (r,c) format for pixels along the line segment
    """
    w = xf - x0
    h = yf - y0
    dx1 = 0
    dy1 = 0
    dx2 = 0
    dy2 = 0
    if w < 0:
        dx1 = -1
        dx2 = -1
    elif w > 0:
        dx1 = 1
        dx2 = 1
    if h < 0:
        dy1 = -1
    elif h > 0:
        dy1 = 1
    longest = abs(w)
    shortest = abs(h)
    if not longest > shortest:
        longest = abs(h)
        shortest = abs(w)
        if h < 0:
            dy2 = -1
        elif h > 0:
            dy2 = 1
        dx2 = 0
    numerator = longest >> 1
    x = x0
    y = y0
    points = []
    for i in range(longest + 1):
        points.append((y, x))
        numerator += shortest
        if not numerator < longest:
            numerator -= longest
            x += dx1
            y += dy1
        else:
            x += dx2
            y += dy2
    return points


def do_OP_SUBSTR(vm):
    """
    >>> s = [b'abcdef', b'', b'']
    >>> do_OP_SUBSTR(s, require_minimal=True)
    >>> print(s)
    [b'de']
    """
    pos = vm.pop_nonnegative()
    length = vm.pop_nonnegative()
    vm.append(vm.pop()[length:length + pos])


def read_natoms(g09_file):
    """
    Opens a g09 log file and returns the number of atoms in the system

    Parameters
    ----------
    g09_file: Path to g09 log file
        File path

    Returns
    -------
    natoms: Integer

    """
    with open(g09_file) as f:
        for line in f:
            if line.strip():
                if line.split()[0] == 'NAtoms=':
                    natoms = int(line.split()[3])
                    break
        return natoms


@classmethod
def getKeyFormat(cls, data):
    """
        Return a type of key.
        """
    key_type = cls._guessStringType(data)
    human_readable = {'public_openssh': 'OpenSSH Public', 'private_openssh':
        'OpenSSH Private', 'public_sshcom': 'SSH.com Public',
        'private_sshcom': 'SSH.com Private', 'private_putty':
        'PuTTY Private', 'public_lsh': 'LSH Public', 'private_lsh':
        'LSH Private'}
    return human_readable.get(key_type, 'Unknown format')


def storyname_to_filename(name: str) ->str:
    """converts the story name to a suitable name for a file on disk"""
    filename = name.lower()
    filename = filename.replace(' ', '_')
    filename = filename.replace('.', '_')
    filename = filename.replace("'", '')
    filename = filename.replace('"', '')
    filename = filename.replace('\\', '')
    filename = filename.replace('/', '')
    filename = filename.replace('*', '')
    return filename


def add(record):
    """Add record to soup.

        @param record: IRecord implementation.
        @return: intid for record.
        """


def __iter__():
    """An iterator over all the mailing lists.

        :return: iterator over `IMailingList`.
        """


def _rehydrate_skeleton_class(skeleton_class, class_dict):
    """Put attributes from `class_dict` back on `skeleton_class`.

    See CloudPickler.save_dynamic_class for more info.
    """
    registry = None
    for attrname, attr in class_dict.items():
        if attrname == '_abc_impl':
            registry = attr
        else:
            setattr(skeleton_class, attrname, attr)
    if registry is not None:
        for subclass in registry:
            skeleton_class.register(subclass)
    return skeleton_class


def get_ver():
    """
    Gather the version number from git
    """
    import subprocess
    proc = subprocess.Popen(['git', 'describe'], stdout=subprocess.PIPE)
    if proc.wait():
        return
    v = proc.stdout.read().decode().strip()
    if '-' not in v:
        ret = v
    else:
        csum = v[v.rindex('-') + 1:]
        base = v[:v.rindex('-')]
        count = base[base.rindex('-') + 1:]
        tag = base[:base.rindex('-')]
        ret = f'{tag}.post{count}+{csum}'
    return ret


def _resolve_name(name, package, level):
    """Return the absolute name of the module to be imported."""
    if not hasattr(package, 'rindex'):
        raise ValueError("'package' not set to a string")
    dot = len(package)
    for x in range(level, 1, -1):
        try:
            dot = package.rindex('.', 0, dot)
        except ValueError:
            raise ValueError(
                'attempted relative import beyond top-level package')
    return '%s.%s' % (package[:dot], name)


def clean_path(source):
    """
    Replace backslashes from source.file_name with a slash
    """
    source.file_name = source.file_name.replace('\\', '/')
    return source


def count_alive_neighbors(grid, x, y):
    """ Count neighbors that are alive. """
    height = len(grid)
    width = len(grid[0])
    alive_count = 0
    for i in range(-1, 2):
        for j in range(-1, 2):
            neighbor_x = x + i
            neighbor_y = y + j
            if i == 0 and j == 0:
                continue
            elif neighbor_x < 0 or neighbor_y < 0 or neighbor_y >= height or neighbor_x >= width:
                alive_count += 1
            elif grid[neighbor_y][neighbor_x] == 1:
                alive_count += 1
    return alive_count


def assert_exc(cond, msg=None, exc=ValueError):
    """ replacement for assert that throws the specified exception instead of AssertionError """
    if not cond:
        raise exc(msg)


def add_prefix_to_lines_but_one(_lines, _prefix):
    """
        add_prefix_to_lines_but_one()
        ________________________________________________________________________

        Add a prefix to each line in _lines.

            e.g. : "abc
"def
" → "abcPREFIXdef
"]
        ________________________________________________________________________

        PARAMETERS :
            ▪ _lines        : a string made of line ending with
.
            ▪ _prefix       : (str)the prefix to be added

        RETURNED VALUE      : a string
    """
    res = []
    _lines = _lines.split('\n')
    for index, line in enumerate(_lines):
        if index == 0:
            res.append(line)
        else:
            res.append(_prefix + line)
    res = '\n'.join(res)
    return res


def nprint(ctx, x, n=6, **kwargs):
    """
        Equivalent to ``print(nstr(x, n))``.
        """
    print(ctx.nstr(x, n, **kwargs))


def runingest(sdms):
    """ Call archive tool or move data to trigger archiving of sdms.
    This function will ultimately be triggered by candidate portal.
    """
    NotImplementedError


def list_placeholder(length, is_pg=False):
    """Returns a (?,?,?,?...) string of the desired length"""
    return '(' + '?,' * (length - 1) + '?)'


@staticmethod
def as_sorted_list(options):
    """
        Returns all options in a list sorted according to their option numbers.

        :return: the sorted list
        """
    if len(options) > 0:
        options = sorted(options, key=lambda o: o.number)
    return options


@staticmethod
def _list_or_dict_subscript(obj, key):
    """
        Helper function to use key as a key if obj is a dict, or as an int index if list.

        :param obj: The object to subscript.
        :param key: The key to subscript with.
        :return: The subscripted object.
        """
    if isinstance(obj, dict):
        return obj[key]
    elif isinstance(obj, list):
        return obj[int(key)]
    else:
        raise TypeError


def col_indices(n, square_length):
    """ Returns the one-dimensional indices of all the cells belonging to the
		same column as index `n`.
	"""
    j = n % square_length
    return range(j, square_length * (square_length - 1) + j + 1, square_length)


@staticmethod
def tree_transitions_from_active_to_unsure_and_best_upstream_neighbor_is_null(
    interface: 'TreeInterfaceDownstream'):
    """
        Tree transitions from Active to Unsure state AND
        BestUpstream neighbor is null
        """
    interface.logger.debug(
        'tree_transitions_from_active_to_unsure_and_best_upstream_neighbor_is_null'
        )
    interface.send_i_am_no_longer_upstream()


def _log(attrs, inputs, proto_obj):
    """Elementwise log of input array."""
    return 'log', attrs, inputs


def _add_doc(func, doc):
    """Add documentation to a function."""
    func.__doc__ = doc


@staticmethod
def moveaxis(tensor, source, destination):
    """Move axes of a tensor to new positions.

        Parameters
        ----------
        tensor : tl.tensor
        source : int or sequence of int
            Original positions of the axes to move. These must be unique.
        destination : int or sequence of int
            Destination positions for each of the original axes. These must also be
            unique.

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


def diff_eq(u, pe, h):
    """
    Basic difference equation.

    1/pe d^2C/dz^2 - dC/dz
    """
    return 1 / pe / h ** 2 * (u[0] - 2 * u[1] + u[2]) - 1 / (2 * h) * (u[2] -
        u[0])


def project_opened(project):
    """This function is called after opening the project"""


def page(cursor, max_results, query):
    """
        Generages a page which contains a subset of the objects in the
        collection.

        :param unicode cursor:
            Used to determine the start point of the page. Defaults to ``None``
            if no cursor was supplied.
        :param int max_results:
            Used to limit the number of results presented in a page. Defaults
            to ``None`` if no limit was specified.
        :param unicode query:
            Search term requested through the API. Defaults to ``None`` if no
            search term was requested.

        :return:
            (cursor, data). ``cursor`` is an opaque string that refers to the
            next page, and is ``None`` if this is the last page. ``data`` is a
            list of all the objects within the page.
        :rtype: tuple
        """


def resample(df, rule, how=None, **kwargs):
    """
    Extends pandas resample methods to index made of integers
    """
    import pandas as pd
    if how == None:
        import numpy as np
        how = np.mean
    if isinstance(df.index, pd.DatetimeIndex) and isinstance(rule, str):
        return df.resample(rule, how, **kwargs)
    else:
        s = (df.index.to_series() / rule).astype(int)
        aux = df.groupby(s).apply(how).set_index(s.index[::rule])
        return aux


@classmethod
def reset_connection_params(cls):
    """Removes all existing connection parameter information."""
    cls._connection_params = {}


def get(name):
    """Make the config values accessible through get()"""
    return globals().get(name)


def methods_equivalent(meth1, meth2):
    """Return True if the two methods are the same implementation."""
    return getattr(meth1, '__func__', meth1) is getattr(meth2, '__func__',
        meth2)


@classmethod
def from_row_tuple(cls, db, t):
    """
        Given a tuple `t` corresponding to a result from a SELECT query,
        this will construct a new instance for this entity. Note that
        the tuple `t` must be in *exact* correspondence with the columns
        returned by `nfldb.Entity.sql_fields`.
        """
    cols = cls.sql_fields()
    seta = setattr
    obj = cls(db)
    for i, field in enumerate(cols):
        seta(obj, field, t[i])
    return obj


def SetVersionNumber(RevString):
    """Set the subversion version number

    :param str RevString: something like "$Revision: 4155 $"
      that is set by subversion when the file is retrieved from subversion.

    Place ``GSASIIpath.SetVersionNumber("$Revision: 4155 $")`` in every python
    file.
    """
    try:
        RevVersion = int(RevString.split(':')[1].split()[0])
        global version
        version = max(version, RevVersion)
    except:
        pass


def _multithreaded_runner(in_queue, out_queue, func):
    """Actual process running in parallel. Accepts a function to perform on
    members of the queue. Only applies the function to all but the first item of
    each member

    :param in_queue: queue which is providing the inputs
    :param out_queue: queue which holds the outputs
    """
    while True:
        in_args = in_queue.get()
        if in_args is None:
            break
        else:
            out_queue.put((in_args[0], func(*in_args[1:])))


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 '+'


def _build_pem(tls_cert):
    """Generate PEM encoded TLS certificate data

    :param tls_cert: TLS certificate
    :returns: PEm encoded certificate data
    """
    pem = ()
    if tls_cert.intermediates:
        for c in tls_cert.intermediates:
            pem = pem + (c,)
    if tls_cert.certificate:
        pem = pem + (tls_cert.certificate,)
    if tls_cert.private_key:
        pem = pem + (tls_cert.private_key,)
    return '\n'.join(pem)


def dict2ckptl(ckdict):
    """
        ckdict = {'__RequestVerificationToken': '9VdrIliI', 'ASP.NET_SessionId': 'epax', 'BIGipServer': 'rd19', 'TS013d8ed5': '0105b6b0', 'TSPD_101': '08819c2a'}
        ckptl = dict2ckptl(ckdict)
        pobj(ckptl)
    """
    return list(ckdict.items())


@classmethod
def from_stream(cls, stream, marker_code, offset):
    """
        Extract the horizontal and vertical dots-per-inch value from the APP1
        header at *offset* in *stream*.
        """
    segment_length = stream.read_short(offset)
    if cls._is_non_Exif_APP1_segment(stream, offset):
        return cls(marker_code, offset, segment_length, 72, 72)
    tiff = cls._tiff_from_exif_segment(stream, offset, segment_length)
    return cls(marker_code, offset, segment_length, tiff.horz_dpi, tiff.
        vert_dpi)


def _perform_threshold_filter(anoms, periodic_max, threshold):
    """
    Filters the list of anomalies per the threshold filter

      anoms : list of floats
        the anoms returned by the algorithm
      periodic_max : float
        calculated daily max value
      threshold : med_max" | "p95" | "p99"
        user-specified threshold value used to filter anoms
    """
    if threshold == 'med_max':
        thresh = periodic_max.median()
    elif threshold == 'p95':
        thresh = periodic_max.quantile(0.95)
    elif threshold == 'p99':
        thresh = periodic_max.quantile(0.99)
    else:
        raise AttributeError(
            'Invalid threshold, threshold options are None | med_max | p95 | p99'
            )
    return anoms.loc[anoms.values >= thresh]


@classmethod
def new_secret(cls):
    """ Placeholder method to create new symmetric secret.
        """
    return super().new_secret(key=bytes(32), seed=None)


def _get_win_folder_from_registry(csidl_name):
    """This is a fallback technique at best. I'm not sure if using the
    registry for this guarantees us the correct answer for all CSIDL_*
    names.
    """
    import _winreg
    shell_folder_name = {'CSIDL_APPDATA': 'AppData', 'CSIDL_COMMON_APPDATA':
        'Common AppData', 'CSIDL_LOCAL_APPDATA': 'Local AppData'}[csidl_name]
    key = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER,
        'Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders'
        )
    dir, type = _winreg.QueryValueEx(key, shell_folder_name)
    return dir


def fix_id(fid):
    """As a temporary measure, escapes certain special characters in a name.

       Right now, a measure like this is required to make Vega* work properly
       with various field names.

       See https://github.com/vega/vega-lite/issues/4965.
    """
    new_id = ''
    for c in fid:
        if c == '.':
            new_id += ':'
        elif c == ']':
            new_id += ')'
        elif c == '[':
            new_id += '('
        elif c == "'" or c == '"':
            pass
        elif c == '\\':
            new_id += '|'
        else:
            new_id += c
    return new_id


@classmethod
def fromtimedelta(cls, delta):
    """
        Return :class:`.Delta` object from a native timedelta object.

        Returns:
            :class:`.Delta`
        """
    return cls(seconds=delta.total_seconds())


def send_highlight_row(s, private_key, url, row_id):
    """Send a SAMP notification to highlight a row in a table."""
    import os
    message = {}
    message['samp.mtype'] = 'table.highlight.row'
    message['samp.params'] = {}
    message['samp.params']['row'] = str(row_id)
    message['samp.params']['url'] = url
    lockfile = os.path.expanduser('~/.samp')
    if not os.path.exists(lockfile):
        raise RuntimeError('A running SAMP hub was not found.')
    else:
        s.samp.hub.notifyAll(private_key, message)


def __get_html(perform_dict):
    """Get innerHTML from element."""
    return True, perform_dict.get('element').get_attribute('innerHTML')


def get_refinement_neighbors(leaf, extent=2):
    """
    Get the list of neighbors used for refinement.
    This combines the neighbor and upper_neighbor list into
    one final list of neighbors

    Parameters
    ----------
    leaf : NDTree.node
        The leaf node we are evaluating

    extent : int
        The extent of the stencil, -extent,...,0,...,extent

    Returns
    -------
    final_list : list
        The final list of neighbors

    """
    offsets, neighbor_indices, neighbors, upper_neighbors = (leaf.
        find_neighbors(extent=extent))
    total_neighbors = len(neighbors)
    final_list = [None] * total_neighbors
    for i in range(total_neighbors):
        if upper_neighbors[i] is not None:
            node = upper_neighbors[i]
            if not node.leaf:
                node = neighbors[i]
            final_list[i] = node
    return final_list


def get_keypair(client, name_or_id):
    """Load a keypair by name or ID."""
    if name_or_id.isdigit():
        return client.get_keypair(name_or_id)
    keypairs = client.get_keypairs({'name': name_or_id})
    if not keypairs:
        return
    return client.reload(keypairs[0])


def _generate_dynamic_values(map, prefix, index_start, index_end, value_start):
    """ Generate values in a map (dict) dynamically. Each key starts with
        a (string) prefix, followed by an index in the inclusive range
        [index_start, index_end]. The values start at value_start.
    """
    for index in range(index_start, index_end + 1):
        name = '%s%s' % (prefix, index)
        value = value_start + index - index_start
        map[name] = value


def with_metaclass(meta, base=object):
    """Create a base class with a metaclass."""
    return meta('NewBase', (base,), {})


def delete_cluster_subnet_group(ClusterSubnetGroupName=None):
    """
    Deletes the specified cluster subnet group.
    See also: AWS API Documentation

    :example: response = client.delete_cluster_subnet_group(
        ClusterSubnetGroupName='string'
    )

    :type ClusterSubnetGroupName: string
    :param ClusterSubnetGroupName: [REQUIRED]
            The name of the cluster subnet group name to be deleted.

    """
    pass


def get_permission_groups(username):
    """Return a list of names of the groups that the user with the
        specified name is a member of.
        """


def search_items(keywords, meta_types=None):
    """ Returns the items from the catalog that have keywords
        in fulltext"""


def keying_set_add():
    """Add a new (empty) Keying Set to the active Scene

    """
    pass


@classmethod
def named_scopes(cls):
    """Return all named Scopes."""
    return [cls.content, cls.settings, cls.user_state, cls.preferences, cls
        .user_info, cls.user_state_summary]


def get_file_name(filepath):
    """
    通过文件路径获得文件名(无论路径是否真正存在对应的文件)
    :param filepath:
    :return:
    """
    strsplit = str(filepath).split('/')
    filename = strsplit[-1]
    return filename


def rollback(tid):
    """Roll back object state to what it was at or before tid."""


def __init__(A):
    """ChargeBearerCode - a model defined in Swagger"""
    A.discriminator = None


@classmethod
def is_overlapping_cds(cls, first, second):
    """
        :param first: first ORF to check for overlap
        :param second: second ORF to check for overlap
        :rtype bool
        """
    if first == second or cls.overlap((first.thick_start, first.thick_end),
        (second.thick_start, second.thick_end)) < 0:
        return False
    return True


def toGraphicsObjectIfPossible(item):
    """Return the item as a QGraphicsObject if possible.

    This function is intended as a workaround for a problem with older
    versions of PyQt (< 4.9), where methods returning 'QGraphicsItem *'
    lose the type of the QGraphicsObject subclasses and instead return
    generic QGraphicsItem wrappers.

    """
    if item is None:
        return None
    obj = item.toGraphicsObject()
    return item if obj is None else obj


def get_parameter_names(parameters):
    """Convert the parameter string supplied by the user into a list of parameter names.

    :param list parameters: The comma separated string of parameter names
    """
    return parameters.replace(' ', '').split(',')


def assert_str(var):
    """Asserts that an object is a str"""
    if not isinstance(var, str):
        raise TypeError(f'variable should be a str, not a {type(var)}')


def parse_browsenode(browse_nodes_response):
    """Parse browse node data and creates a dict.

    Args:
        browse_nodes_response (list): List of browse nodes responses.

    Returns:
        dict: Dict with browse node information.
    """
    mapped_response = {}
    for browse_node in browse_nodes_response:
        mapped_response[browse_node.id] = browse_node
    return mapped_response


def deprecate_thing_type(thingTypeName=None, undoDeprecate=None):
    """
    Deprecates a thing type. You can not associate new things with deprecated thing type.
    See also: AWS API Documentation

    :example: response = client.deprecate_thing_type(
        thingTypeName='string',
        undoDeprecate=True|False
    )

    :type thingTypeName: string
    :param thingTypeName: [REQUIRED]
            The name of the thing type to deprecate.

    :type undoDeprecate: boolean
    :param undoDeprecate: Whether to undeprecate a deprecated thing type. If true , the thing type will not be deprecated anymore and you can associate it with things.

    :rtype: dict
    :return: {}

    """
    pass


def open(credentials):
    """Returns an IFileSystem.

        Should raise zope.security.interfaces.Unauthorized if the user
        can not be authenticated.

        Credentials are passed as (username, password) tuples.
        """


def convert_to_timezone_naive(time_to_freeze):
    """
    Converts a potentially timezone-aware datetime to be a naive UTC datetime
    """
    if time_to_freeze.tzinfo:
        time_to_freeze -= time_to_freeze.utcoffset()
        time_to_freeze = time_to_freeze.replace(tzinfo=None)
    return time_to_freeze


def _isQObjectAlive(obj):
    """An approximation of PyQt's isQObjectAlive().
    """
    try:
        if hasattr(obj, 'parent'):
            obj.parent()
        elif hasattr(obj, 'parentItem'):
            obj.parentItem()
        else:
            raise Exception(
                'Cannot determine whether Qt object %s is still alive.' % obj)
    except RuntimeError:
        return False
    else:
        return True


def brute_force_search(notebook, query):
    """Return all notes in `notebook` that match `query`.

    Returns a sequence of Note objects that match the given search query.

    Arguments:

    notebook - the notebook to search (NoteBook)

    query - the query to search for (string)

    This is implemented as a standalone function so it can easily be replaced
    with some other function that does the search differently (e.g. you could
    have a config option to choose between different search implementations).

    This implementation does a brute force search that simply reads every file
    in the notebook looking for the search words.

    """
    search_words = query.strip().split()
    matching_notes = []
    for note in notebook:
        match = True
        for search_word in search_words:
            if search_word.islower():
                in_title = search_word in note.title.lower()
                in_contents = search_word in note.contents.lower()
            else:
                in_title = search_word in note.title
                in_contents = search_word in note.contents
            if not in_title and not in_contents:
                match = False
        if match:
            matching_notes.append(note)
    return matching_notes


def _welford(data):
    """Welford's method of calculating the running variance.

    This calculates the second moment M2 = sum( (x-m)**2 ) where m=mean of x.
    Returns (n, M2) where n = number of items.
    """
    data = iter(data)
    n = 0
    M2 = 0.0
    try:
        m = next(data)
        n = 1
    except StopIteration:
        pass
    else:
        for n, x in enumerate(data, 2):
            delta = x - m
            m += delta / n
            M2 += delta * (x - m)
    assert M2 >= 0.0
    return n, M2


def triangle(x, loc=0, size=0.5, area=1):
    """
    The triangle window function centered in loc, of given size and area, evaluated at a point.

    Args:
        x: The point where the function is evaluated.
        loc: The position of the peak.
        size: The total.
        area: The area below the function.

    Returns:
        The value of the function.

    """
    return 0 if abs((x - loc) / size) > 1 else (1 - abs((x - loc) / size)
        ) * abs(area / size)


def instoc(Imin, CTR=1, Ki=0.5):
    """
    Instantaneous OverCurrent Pickup Calculator

    Using a sensetivity factor and the CTR, evaluates
    the secondary-level pickup setting for an
    instantaneous overcurrent element.

    Parameters
    ----------
    Imin:       float
                The minimum fault current in primary amps.
    CTR:        float, optional
                Current Transformer Ratio, default=1
    Ki:         Sensetivity factor, default=0.5

    Returns
    -------
    Ipu:        float
                The pickup setting for the instantaneous
                overcurrent element as referred to the
                secondary side.
    """
    Ipu = Ki * abs(Imin) / CTR
    return Ipu


def get_substring():
    """
    Utility function that is designed to make it easier to work with
    get_varnames, get_info, and get_varattribs.

    currently not implemented
    """
    raise NotImplementedError


def next_batch(mgr, batch_size, qname='input'):
    """*DEPRECATED*. Use TFNode.DataFeed class instead."""
    raise Exception('DEPRECATED: Use TFNode.DataFeed class instead')


def _calculate_block_start(current_position):
    """ converts pointer position into one being multiple of 8

        Parameters
        ----------------
        current_position : int
            position in file

        Returns
        -----------
        position multiple of 8
    """
    remain = current_position % 8
    if not remain == 0:
        return current_position - remain + 8
    else:
        return current_position


def del_inline_form_field(inline_form, old_entries, field_name):
    """
    Delete a field from an inline form
    :param inline_form: the inline form from which the field will be deleted
    :param old_entries: Existing lines of the inline form
    :param field_name:  Name of the field to be deleted
    :return: None
    """
    if hasattr(inline_form, field_name):
        delattr(inline_form, field_name)
    for entry in old_entries:
        if hasattr(entry.form, field_name):
            delattr(entry.form, field_name)
        if hasattr(entry.form._fields, field_name):
            delattr(entry.form._fields, field_name)


def renderRows():
    """Render HTML rows for the self.items.

        Uses renderRow and getItems."""


def get_keywords():
    """Get the keywords needed to look up the version information."""
    git_refnames = '$Format:%d$'
    git_full = '$Format:%H$'
    git_date = '$Format:%ci$'
    keywords = {'refnames': git_refnames, 'full': git_full, 'date': git_date}
    return keywords


def repr_spark_context_html(sc):
    """
    Carry over from the Spark 2.2.0 _repr_html_ for spark contexts

    Works on objects whose fully qualified name is 'pyspark.context.SparkContext'
    """
    return (
        """

SparkContext

Spark UI

Version

v{sc.version}

Master

{sc.master}

AppName

{sc.appName}

    """
        .format(sc=sc))


@staticmethod
def _custom_opts():
    """Returns command options in a form that can be handled by the
        command parser."""
    opts = {'-h': {'type': bool, 'default': False}, '-set': {'type': bool,
        'default': False}, '-unset': {'type': bool, 'default': False}}
    return opts


@classmethod
def list_user(cls, user, drop_incompat_keys=False):
    """
        Patch a list user object, example in
        :meth:`Client.user_following`, :meth:`Client.user_followers`, :meth:`Client.search_users`
        """
    user['id'] = str(user['pk'])
    user['profile_picture'] = user['profile_pic_url']
    if drop_incompat_keys:
        cls._drop_keys(user, ['byline', 'follower_count',
            'friendship_status', 'has_anonymous_profile_picture',
            'has_chaining', 'is_favorite', 'is_private', 'is_unpublished',
            'is_verified', 'mutual_followers_count', 'pk',
            'profile_pic_url', 'social_context', 'unseen_count'])
    return user


def coherent_gain(wnd):
    """ Coherent Gain, normalized by len(wnd). """
    return sum(wnd) / len(wnd)


def add_comma(_index, _value):
    """Adds a comma if index > 0, used in loops when making lists."""
    if _index > 0:
        return ', ' + _value
    else:
        return _value


def near(a, b, significance=0.0001):
    """ Returns True iff the difference between the values is within the factor 'significance' of
    one of the original values.  Default is to within 4 decimal places. """
    return abs(a - b) <= significance * max(abs(a), abs(b))


@classmethod
def can_solve(cls, resource):
    """Tells if the solver is able to resolve the given resource.

        Arguments
        ---------
        resource : subclass of ``dataql.resources.Resource``
            The resource to check if it is solvable by the current solver class

        Returns
        -------
        boolean
            ``True`` if the current solver class can solve the given resource, ``False`` otherwise.

        Example
        -------

        >>> AttributeSolver.solvable_resources
        (<class 'dataql.resources.Field'>,)
        >>> AttributeSolver.can_solve(Field('foo'))
        True
        >>> AttributeSolver.can_solve(Object('bar'))
        False

        """
    for solvable_resource in cls.solvable_resources:
        if isinstance(resource, solvable_resource):
            return True
    return False


def authGSSServerInit(service):
    """
    Initializes a context for GSSAPI server-side authentication with the given service principal.
    authGSSServerClean must be called after this function returns an OK result to dispose of
    the context once all GSSAPI operations are complete.

    @param service: a string containing the service principal in the form 'type@fqdn'
        (e.g. 'imap@mail.apple.com').
    @return: a tuple of (result, context) where result is the result code (see above) and
        context is an opaque value that will need to be passed to subsequent functions.
    """


@classmethod
def from_message(cls, message):
    """
        Create a public blob from a network `.Message`.

        Specifically, a cert-bearing pubkey auth packet, because by definition
        OpenSSH-style certificates 'are' their own network representation."
        """
    type_ = message.get_text()
    return cls(type_=type_, blob=message.asbytes())


def _most_constraining(constraining, target_aspect):
    """Find most constraining dimension to scale."""
    if constraining[0] / float(constraining[1]) >= target_aspect[0] / float(
        target_aspect[1]):
        return 1
    else:
        return 0


def parameters_ui(layout, params):
    """ Create the ui for the rig parameters.
    """
    r = layout.row()
    r.prop(params, 'make_control')
    r = layout.row()
    r.prop(params, 'make_widget')
    r = layout.row()
    r.prop(params, 'make_deform')


def parents(node):
    """Returns a 2-tuple of parent nodes for a node.

        Values will be ``nullid`` if the parent is empty.
        """


def _is_public(member_name):
    """Determine visibility of a member based on its name."""
    return not (member_name.startswith('_') and member_name != '__init__')


def check(name, type, country, geometry, **args):
    """Should return True if this area is NEW, False if we should match"""
    return False


def set_target_audiences(target_audiences):
    """Updates the list of target_audiences"""


def output_to_excel(outputs: dict, sheet, output_dict: dict):
    """ output_to_excel(dict, openpyxl.sheet, dict)

		Takes the output dictionary produced by multiple hubs, and
		writes specific fields to the passed excel sheet based on
		the output dictionary passed.

		Args:
			outputs(dict): The dictionary produced by multiple_hubs.

			sheet(openpyxl.sheet): The excel sheet to be written to.

			output_dict(dict): A dictionary that links keys found in the
					   outputs dict, and the column that they should
					   appear in, within the sheet.

	"""
    cols = list(sheet.iter_cols(min_col=1, max_col=1))[0]
    first_empty_row = len(cols) + 1
    sheet.merge_cells('A{}:B{}'.format(first_empty_row, first_empty_row))
    sheet.merge_cells('C{}:D{}'.format(first_empty_row, first_empty_row))
    sheet.merge_cells('E{}:F{}'.format(first_empty_row, first_empty_row))
    sheet.merge_cells('G{}:H{}'.format(first_empty_row, first_empty_row))
    for key, value in output_dict.items():
        try:
            sheet['{}{}'.format(value, first_empty_row)] = outputs[key]
        except:
            continue


def parse_int(arg, name, parser):
    """
    Tries to parse a string as an integer.

    :param arg:    The arg to parse as an integer
    :param name:   A description of the argument used for error messages
    :param parser: The parser used to report an error message
    :return: The parsed integer if succeeds or None if fails
    """
    if arg:
        try:
            arg = int(arg)
            return arg
        except ValueError:
            parser.error("'" + arg + "' is not a valid " + name)
            return None


def _get_best_indexes(logits, n_best_size):
    """Get the n-best logits from a list."""
    index_and_score = sorted(enumerate(logits), key=lambda x: x[1], reverse
        =True)
    best_indexes = []
    for i in range(len(index_and_score)):
        if i >= n_best_size:
            break
        best_indexes.append(index_and_score[i][0])
    return best_indexes


def write_parameters_to_file(params, filename):
    """ Writes all parameters stored in a given Parameters object
        to a given file in the following format format:
        <param_name> {param_value}. If the file
        already exists, all parameters will be over-written with the new ones.

        Args:
            params (Parameters): parameters object.
            filename (str): file name where parameters must be written.
    """
    file_with_params = open(filename, 'w')
    for name, value in params.params.items():
        file_with_params.write('<' + name + '> {' + value + '}\n')
    file_with_params.close()


def make_path_safe(path):
    """
    Make a path safe for use in Cypher

    Parameters
    ----------
    path : str
        Path to sanitize

    Returns
    -------
    str
        Cypher-safe path
    """
    return path.replace('\\', '/').replace(' ', '%20')


def processExited(reason):
    """
        Called when the child process exits.

        @type reason: L{twisted.python.failure.Failure}
        @param reason: A failure giving the reason the child process
            terminated.  The type of exception for this failure is either
            L{twisted.internet.error.ProcessDone} or
            L{twisted.internet.error.ProcessTerminated}.

        @since: 8.2
        """


def get_delta(vmag_matrix, deriv_step):
    """ Convert voltage mag. data into the change in voltage mag. wrt time.

    This function finds the change in voltage magntiude between
    time-points. The spacing between time-points is a variable that can be
    changed. The change in voltage magnitude is calculated as Delta_Vmag =
    Vmag(node x, time t) - Vmag(node x, time t-deriv_step).

    Args
    -------
    vmag_matrix: 2D ndarray, shape (# of data-points, # of nodes)
        This is the voltage magnitude data at different nodes. We want to
        transform this data so we end up with the change
        in voltage magnitude from one time-point to the next for each node.

    deriv_step: int
        This parameter decides how many time-points for which
        to compute the voltage magntiude difference at a node. Ex. if
        deriv_step = 5, then the change in voltage magnitude will be
        Delta_Vmag = Vmag(node x, time t) - Vmag(node x, time t-5).

    Returns
    -------
    vmag_matrix: 2D ndarray, shape (# of data-points, # of nodes)
        This matrix now represents the change in voltage
        magnitude at a node from one time-point to the next. Each column
        corresponds to a different node and each row to a time-point.
    """
    end = len(vmag_matrix[:, (1)]) - 1
    vmag_matrix = vmag_matrix[deriv_step + 1:-1, :] - vmag_matrix[1:end -
        deriv_step, :]
    return vmag_matrix


def parameters_ui(layout, params):
    """ Create the ui for the rig parameters.
    """
    r = layout.row()
    r.prop(params, 'make_rotations')
    r = layout.row()
    r.prop(params, 'tweak_extra_layers')
    r.active = params.tweak_extra_layers
    col = r.column(align=True)
    row = col.row(align=True)
    for i in range(8):
        row.prop(params, 'tweak_layers', index=i, toggle=True, text='')
    row = col.row(align=True)
    for i in range(16, 24):
        row.prop(params, 'tweak_layers', index=i, toggle=True, text='')
    col = r.column(align=True)
    row = col.row(align=True)
    for i in range(8, 16):
        row.prop(params, 'tweak_layers', index=i, toggle=True, text='')
    row = col.row(align=True)
    for i in range(24, 32):
        row.prop(params, 'tweak_layers', index=i, toggle=True, text='')


def anyrevs(specs, user=False, localalias=None):
    """Find revisions matching one of the given revsets."""


def link(detach: bool=False):
    """Use the mouse to create a link between two nodes

    :param detach: Detach, Detach and redirect existing links
    :type detach: bool
    """
    pass


def normalize_correlation(coincidence_count, coachfile_count):
    """Calculate a correlation between 0 and 1 for scoring.

    Essentially, we are calculating:

        P(Y|X) = times_XY_committed_together/times_X_committed

    Example:

    >>> normalize_correlation(3, 6)
    0.5
    """
    return 1.0 * coincidence_count / coachfile_count


def as_unicode(s):
    """Return s if unicode string, or decode bytes to unicode string using utf-8."""
    if isinstance(s, bytes):
        return s.decode('utf-8')
    else:
        return s


def create_sat_smti(instance):
    """Creates a CNF SAT encoding of an SMTI instance.
    :raises NotImplementedError: Raised if the instance is not an SMTI instance
    """
    if not instance.is_SMTI():
        raise NotImplementedError
    varmap = {}
    var_count = 1
    for agent in instance.single_residents:
        pref_length = 1
        for pref_group in agent.preferences:
            for pref in pref_group:
                varmap['r', agent.id, pref_length] = var_count
                var_count += 1
                pref_length += 1
        varmap['r', agent.id, pref_length] = var_count
        var_count += 1
    for agent in instance.single_residents:
        pref_length = 1
        for pref_group in agent.preferences:
            for pref in pref_group:
                varmap['h', agent.id, pref_length] = var_count
                var_count += 1
                pref_length += 1
        varmap['h', agent.id, pref_length] = var_count
        var_count += 1
    encoding = ''
    for resident in instance.single_residents:
        encoding += '%d 0' % varmap['r', resident.id, 1]
    for hospital in instance.hospitals:
        encoding += '%d 0' % varmap['h', hospital.id, 1]
    for resident in instance.single_residents:
        for pref in range(resident.num_preferences):
            encoding += '%d -%d 0' % (varmap['r', resident.id, pref],
                varmap['r', resident.id, pref] + 1)
    for hospital in instance.single_hospitals:
        for pref in range(hospital.num_preferences):
            encoding += '%d -%d 0' % (varmap['h', hospital.id, pref],
                varmap['h', hospital.id, pref] + 1)
    for hospital in instance.hospitals:
        for resident in instance.single_residents:
            p = resident.position_of(hospital)
            q = hospital.position_of(resident)
            encoding += '-%d %d %d 0' % (varmap['r', resident.id, p],
                varmap['r', resident.id, p] + 1, varmap['h', hospital.id, q])
            encoding += '-%d %d -%d 0' % (varmap['r', resident.id, p],
                varmap['r', resident.id, p] + 1, varmap['h', hospital.id, q
                ] + 1)
            encoding += '-%d %d %d 0' % (varmap['h', hospital.id, q],
                varmap['h', hospital.id, q] + 1, varmap['r', resident.id, p])
            encoding += '-%d %d -%d 0' % (varmap['h', hospital.id, q],
                varmap['h', hospital.id, q] + 1, varmap['r', resident.id, p
                ] + 1)


@classmethod
def cache_key(cls, params):
    """
        This method computes an hash to use as a part of the key in the cache starting from the parameters used by the
        feature.
        @return: The hash of the parameters used by the feature.
        :param params:
        """
    p = params.copy()
    p.update({'': str(cls)})
    return str(p).replace("'", '')


def g(*args):
    """REGEX: build group."""
    txt = ''
    for arg in args:
        if txt == '':
            txt = arg
        else:
            txt += '|' + arg
    return '(' + txt + ')'


@classmethod
def reV_run(cls, points_control, site_df, cf_file, cf_year, output_request=
    ('lcoe_fcr',)):
    """Execute SAM LCOE simulations based on a reV points control instance.

        Parameters
        ----------
        points_control : config.PointsControl
            PointsControl instance containing project points site and SAM
            config info.
        site_df : pd.DataFrame
            Dataframe of site-specific input variables. Row index corresponds
            to site number/gid (via df.loc not df.iloc), column labels are the
            variable keys that will be passed forward as SAM parameters.
        cf_file : str
            reV generation capacity factor output file with path.
        cf_year : int | str | None
            reV generation year to calculate econ for. Looks for cf_mean_{year}
            or cf_profile_{year}. None will default to a non-year-specific cf
            dataset (cf_mean, cf_profile).
        output_request : list | tuple | str
            Output(s) to retrieve from SAM.

        Returns
        -------
        out : dict
            Nested dictionaries where the top level key is the site index,
            the second level key is the variable name, second level value is
            the output variable value.
        """
    out = {}
    site_gids, calc_aey, cf_arr = cls._parse_lcoe_inputs(site_df, cf_file,
        cf_year)
    for site in points_control.sites:
        _, inputs = points_control.project_points[site]
        site_df = cls._get_annual_energy(site, site_df, site_gids, cf_arr,
            inputs, calc_aey)
        out[site] = super().reV_run(site, site_df, inputs, output_request)
    return out


def report_drift_missing(results, state_properties):
    """
    Prints missing results in Query Results between two states.

    :type results: List of List of Strings.
    :param results: Deviation information.
    :return: None
    """
    print('Missing Query Results:')
    print()
    for result in results:
        for field, value in zip(state_properties, result):
            print(field, ': ', value)
        print()


def __call__(request, c):
    """In the absence of method specific attributes or if the resolver
        cannot find an instance attribute to apply the handler call back, the
        object will simply be called.

        ``request``,
            Plugin instance implementing :class:`IHTTPRequest` interface.

        ``c``,
            Dictionary like Context object. Typically populated by
            :class:`IHTTPResource` and view-callable. Made availabe inside
            HTML templates.
        """


@staticmethod
def _get_all_parent_nodes(path, delimiter='/'):
    """Get path to all parent nodes for current leaf, starting from leaf's direct parent down to root"""
    pos = path.rfind(delimiter)
    while pos != -1:
        yield path[:pos + 1]
        pos = path.rfind(delimiter, 0, pos)


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

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

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


def drop_last_in_list(lst):
    """
    Returns the index names without NaN values inside
    :param xl: Pandas DataFrame
    :return: a list of index labels
    """
    new_list = list()
    for i in lst:
        i.pop()
        new_list.append(i)
    return new_list


def _string_rpc_registration_request(rpc):
    """Converts ServerOriginatedRPC or RPCSignature to a string."""
    if rpc is None:
        return None
    args = sorted(map(lambda x: x.name, rpc.arguments))
    return rpc.name + '(' + ','.join(args) + ')'


def getLastAccessed():
    """
        Return the time the transient object was last accessed in
        integer seconds-since-the-epoch form.  Last accessed time
        is defined as the last time the transient object's container
        "asked about" this transient object.
        """


def parents_of(item):
    """
    Returns list of parents (i.e. object.parent values) starting from the top one (Session)
    """
    parents = [item]
    current = item
    while current.parent is not None:
        parents.append(current.parent)
        current = current.parent
    return parents[::-1]


@staticmethod
def pre_save(sender, instance, **kwargs):
    """
        If there is no state set, default to the start.
        """
    if instance.state is None:
        instance.state = instance.start


def vector_dot(v1, v2):
    """
    Calculate vector dot product of two vectors.

    Parameters
    ----------
    v1,v2 : ndarray
        The arrays to calculate the product from.
        The vectors are represented by the last axis.

    Returns
    -------
    product : float or ndarray
        Scalar product over the last dimension of the arrays.
    """
    return (v1 * v2).sum(axis=-1)


def ax_labeling(ax, **kwargs):
    """
    Set labels and title for a plot
    :param ax: should be a class 'matplotlib.axes._subplots.AxesSubplot'
    :param kwargs: accepting x/y/z, x/y/zl, x/y/zlabel, x/y/z_label, title
    :return: None
    """
    if len(kwargs) is 0:
        return None
    x_label, y_label, z_label = None, None, None
    title = None
    if 'x' in kwargs:
        x_label = kwargs.get('x')
    if 'xl' in kwargs:
        x_label = kwargs.get('xl')
    if 'xlabel' in kwargs:
        x_label = kwargs.get('xlabel')
    if 'x_label' in kwargs:
        x_label = kwargs.get('x_label')
    if 'y' in kwargs:
        y_label = kwargs.get('y')
    if 'yl' in kwargs:
        y_label = kwargs.get('yl')
    if 'ylabel' in kwargs:
        y_label = kwargs.get('ylabel')
    if 'y_label' in kwargs:
        y_label = kwargs.get('y_label')
    if 'z' in kwargs:
        z_label = kwargs.get('z')
    if 'zl' in kwargs:
        z_label = kwargs.get('zl')
    if 'zlabel' in kwargs:
        z_label = kwargs.get('zlabel')
    if 'z_label' in kwargs:
        z_label = kwargs.get('z_label')
    if 't' in kwargs:
        title = kwargs.get('t')
    if 'title' in kwargs:
        title = kwargs.get('title')
    if x_label is not None:
        ax.set_xlabel(x_label)
    if y_label is not None:
        ax.set_ylabel(y_label)
    if z_label is not None:
        ax.set_zlabel(z_label)
    if title is not None:
        ax.set_title(title)
    return None


@classmethod
def address_to_hashX(cls, address):
    """Return a hashX given a coin address."""
    return cls.hashX_from_script(cls.pay_to_address_script(address))


def process_rr(io, data, record_type, record_keys, name, print_name):
    """ Print out single line record entries """
    if data is None:
        return
    if isinstance(record_keys, str):
        record_keys = [record_keys]
    elif not isinstance(record_keys, list):
        raise ValueError('record_keys must be a string or list of strings')
    name_display = name if print_name else ' ' * len(name)
    print('{} {} IN {} '.format(name_display, data['ttl'], record_type),
        end='', file=io)
    for i, key in enumerate(record_keys):
        print(data[key], end='\n' if i == len(record_keys) - 1 else ' ',
            file=io)


def write_umi_in_header(reads, umi):
    """
    Copy the UMI in the header separated by an _ to use later with UMI_tools.

    params: seq
    params: config
    params: adaptator
    """
    header = reads.header.split(' ')
    header[0] += '_' + str(umi['seq'])
    reads.header = ' '.join(header)
    return reads


def myfoo(i, s, course=['s'], gg=2, fdf=3, **d):
    """
    return course id
    Keyword arguments,
    course, -- course object (default None)
    """
    print('data:', d)
    if course:
        print(course)
        return course.get('id')
    else:
        print('No Course!')
        return None


@classmethod
def supports_to_pcm(cls):
    """returns True if all necessary components are available
        to support the .to_pcm() method"""
    try:
        from audiotools.decoders import OpusDecoder
        return True
    except ImportError:
        return False


@staticmethod
def _check_compat(sock_info):
    """Check whether the server version in-use supports aggregation."""
    pass


def is_namespace_mutable(context, namespace):
    """Return True if the namespace is mutable in this context."""
    if context.is_admin:
        return True
    if context.owner is None:
        return False
    return namespace.owner == context.owner


@staticmethod
def get_all_parents(node_pks, return_values=('id',)):
    """Get all the parents of given nodes

        :param node_pks: one node pk or an iterable of node pks
        :return: a list of aiida objects with all the parents of the nodes"""
    from aiida.orm import Node, QueryBuilder
    q_build = QueryBuilder()
    q_build.append(Node, tag='low_node', filters={'id': {'in': node_pks}})
    q_build.append(Node, with_descendants='low_node', project=return_values)
    return q_build.all()


def module_join(parent: str, child: str) ->str:
    """Join module ids, accounting for a possibly empty parent."""
    if parent:
        return parent + '.' + child
    else:
        return child


def _task_matcher(details):
    """Matches task details emitted."""
    if not details:
        return False
    if 'task_name' in details and 'task_uuid' in details:
        return True
    return False


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


@classmethod
def get_base_categories(cls, store):
    """Returns all available base categories
        :param store: a store
        :returns: categories
        """
    return store.find(cls, category_id=None)


def print_header():
    """
    Prints the header for the normal phase of a benchmark.
    """
    print('{:<12s} {:<12s} {:<12s} ( {:<12s} )'.format('', 'user', 'system',
        'real'))


@classmethod
def _append_error(cls, err_message):
    """
        Appends the passed error message to the class error list

        :param err_message: the error message to append
        """
    if not cls._parse_errors:
        cls._parse_errors = []
    cls._parse_errors.append(err_message)


def modifier_move_up(modifier: str=''):
    """Move modifier up in the stack

    :param modifier: Modifier, Name of the modifier to edit
    :type modifier: str
    """
    pass


@staticmethod
def qindex2index(index):
    """ from a QIndex (row/column coordinate system), get the buffer index of the byte """
    r = index.row()
    c = index.column()
    if c > 16:
        return 16 * r + c - 17
    else:
        return 16 * r + c


def nernst_planck_flux(c, gcx, gcy, gvx, gvy, ux, uy, D, z, T, p, mu=0.0):
    """
     Calculate the flux component of the Nernst-Planck equation

     Parameters
     ------------

    c:     concentration
    gcx:   concentration gradient, x component
    gcy:   concentration gradient, y component
    gvx:   voltage gradient, x component
    gvy:   voltage gradient, y component
    ux:    fluid velocity, x component
    uy:    fluid velocity, y component
    D:     diffusion constant, D
    z:     ion charge
    T:     temperature
    p:     parameters object

    Returns
    --------
    fx, fx        mass flux in x and y directions
    """
    alpha = D * z * p.q / (p.kb * T)
    fx = -D * gcx - alpha * gvx * c + ux * c - mu * c * gvx
    fy = -D * gcy - alpha * gvy * c + uy * c - mu * c * gvy
    return fx, fy


def simplify(units):
    """
    Cancel units that appear in both numerator and denominator, then attempt to replace
    groups of units with single units where possible (ie, J/s => W)
    """
    pass


@classmethod
def create_engine(cls, *args, **kwargs):
    """
        Return engine from :func:`sqlalchemy.create_engine`

        Parameters
        ----------
        *args : any
            Positional arguments to :func:`sqlalchemy.create_engine`.
        **kwargs : any
            Keyword arguments to :func:`sqlalchemy.create_engine`.

        See Also
        --------
        :func:`sqlalchemy.create_engine`

        Returns
        -------
        :class:`SQLAlchemy` engine

        """
    from sqlalchemy import create_engine
    return create_engine(*args, **kwargs)


@classmethod
def download(cls, url: str) ->str:
    """Should get the raw snippet content from url and return a path to temporary file or directory"""
    raise NotImplementedError


def pro_add_qt_dependencies(target_config, metadata, pro_lines, debug=None):
    """ Add the Qt dependencies of a module to a .pro file.  target_config is
    the target configuration.  metadata is the module's meta-data.  pro_lines
    is the list of lines making up the .pro file that is updated.  debug is set
    if debug, rather than release, mode is to be used.  If it is None then the
    mode is taken from the target configuration.
    """
    if debug is None:
        debug = target_config.debug
    add = []
    remove = []
    for qt in metadata.qmake_QT:
        if qt.startswith('-'):
            remove.append(qt[1:])
        else:
            add.append(qt)
    if len(remove) != 0:
        pro_lines.append('QT -= %s' % ' '.join(remove))
    if len(add) != 0:
        pro_lines.append('QT += %s' % ' '.join(add))
    pro_lines.append('CONFIG += %s' % ('debug' if debug else 'release'))
    if metadata.cpp11:
        pro_lines.append('CONFIG += c++11')
    pro_lines.extend(target_config.qmake_variables)


def grad(lazyFn):
    """Lazy evaluation of 2D gradient operator on a scalar field"""
    return lazyFn.fn_gradient(0), lazyFn.fn_gradient(1)