def mixture(params, ns, s1, s2, sel_dist1, sel_dist2, theta, pts,
    exterior_int=True):
    """
    Weighted summation of 1d and 2d distributions that share parameters.
    The 1d distribution is equivalent to assuming selection coefficients are
    perfectly correlated.

    params: Parameters for potential optimization.
            It is assumed that last parameter is the weight for the 2d dist.
            The second-to-last parameter is assumed to be the correlation
                coefficient for the 2d distribution.
            The remaining parameters as assumed to be shared between the
                1d and 2d distributions.
    ns: Ignored
    s1: Cache1D object for 1d distribution
    s2: Cache2D object for 2d distribution
    sel_dist1: Univariate probability distribution for s1
    sel_dist2: Bivariate probability distribution for s2
    theta: Population-scaled mutation rate
    pts: Ignored
    exterior_int: If False, do not integrate outside sampled domain.
    """
    fs1 = s1.integrate(params[:-2], None, sel_dist1, theta, None, exterior_int)
    fs2 = s2.integrate(params[:-1], None, sel_dist2, theta, None, exterior_int)
    p2d = params[-1]
    return (1 - p2d) * fs1 + p2d * fs2


def free_bake():
    """Delete physics bake

    """
    pass


def rafmt(x, pos):
    """Formatter function for Right Ascension."""
    return u'{:.0f}°'.format(x)


def no_normalization(data):
    """
    Does nothing. Returns original data
    """
    return data


def ptr_byref(args, offset=-1):
    """Return the pointer argument passed in by-reference."""
    return args[offset]._obj


@classmethod
def _inverse_covariance(cls, Phi, covariance, EPS_Sigma=1e-07, EPS_residual
    =0.001):
    """Computes the unbiased inverse of an estimated covariance matrix, using the `Hartlap, Simon & Schneider (2007) <https://arxiv.org/abs/astro-ph/0608064>`__ correction factor. See equations (13) and (15) in |Leclercqetal2019|_.

        Parameters
        ----------
        Phi : array, double, dimension=(N,P)
            a set of N simulations
        covariance : array, double, dimension=(P,P)
            estimated covariance matrix
        EPS_Sigma : double, optional, default=1e-7
            epsilon to be added to the diagonal of the covariance matrix, if necessary
        EPS_residual : double, optional, default=1e-3
            maximum residual in covariance*covariance^{-1} before attempting regularization

        Returns
        -------
        Sigma^hat_theta^{-1}' : array, double, dimension=(P,P)
            alpha*(Sigma^hat_theta')^{-1} where (Sigma^hat_theta')^{-1} is the inverse sample covariance

        """
    from pyselfi.utils import regular_inv
    N = Phi.shape[0]
    P = Phi.shape[1]
    alpha = (N - P - 2.0) / (N - 1.0) if P < N - 2 else 1.0
    inv_covariance = regular_inv(covariance, EPS_Sigma, EPS_residual)
    return alpha * inv_covariance


def _get_all_gnupg_options():
    """Get all GnuPG options and flags.

    This is hardcoded within a local scope to reduce the chance of a tampered
    GnuPG binary reporting falsified option sets, i.e. because certain options
    (namedly the ``--no-options`` option, which prevents the usage of gpg.conf
    files) are necessary and statically specified in
    :meth:`gnupg._meta.GPGBase._make_args`, if the inputs into Python are
    already controlled, and we were to summon the GnuPG binary to ask it for
    its options, it would be possible to receive a falsified options set
    missing the ``--no-options`` option in response. This seems unlikely, and
    the method is stupid and ugly, but at least we'll never have to debug
    whether or not an option *actually* disappeared in a different GnuPG
    version, or some funny business is happening.

    These are the options as of GnuPG 1.4.12; the current stable branch of the
    2.1.x tree contains a few more -- if you need them you'll have to add them
    in here.

    :type gnupg_options: frozenset
    :ivar gnupg_options: All known GPG options and flags.
    :rtype: frozenset
    :returns: ``gnupg_options``
    """
    three_hundred_eighteen = (
        """
--allow-freeform-uid              --multifile
--allow-multiple-messages         --no
--allow-multisig-verification     --no-allow-freeform-uid
--allow-non-selfsigned-uid        --no-allow-multiple-messages
--allow-secret-key-import         --no-allow-non-selfsigned-uid
--always-trust                    --no-armor
--armor                           --no-armour
--armour                          --no-ask-cert-expire
--ask-cert-expire                 --no-ask-cert-level
--ask-cert-level                  --no-ask-sig-expire
--ask-sig-expire                  --no-auto-check-trustdb
--attribute-fd                    --no-auto-key-locate
--attribute-file                  --no-auto-key-retrieve
--auto-check-trustdb              --no-batch
--auto-key-locate                 --no-comments
--auto-key-retrieve               --no-default-keyring
--batch                           --no-default-recipient
--bzip2-compress-level            --no-disable-mdc
--bzip2-decompress-lowmem         --no-emit-version
--card-edit                       --no-encrypt-to
--card-status                     --no-escape-from-lines
--cert-digest-algo                --no-expensive-trust-checks
--cert-notation                   --no-expert
--cert-policy-url                 --no-force-mdc
--change-pin                      --no-force-v3-sigs
--charset                         --no-force-v4-certs
--check-sig                       --no-for-your-eyes-only
--check-sigs                      --no-greeting
--check-trustdb                   --no-groups
--cipher-algo                     --no-literal
--clearsign                       --no-mangle-dos-filenames
--command-fd                      --no-mdc-warning
--command-file                    --no-options
--comment                         --no-permission-warning
--completes-needed                --no-pgp2
--compress-algo                   --no-pgp6
--compression-algo                --no-pgp7
--compress-keys                   --no-pgp8
--compress-level                  --no-random-seed-file
--compress-sigs                   --no-require-backsigs
--ctapi-driver                    --no-require-cross-certification
--dearmor                         --no-require-secmem
--dearmour                        --no-rfc2440-text
--debug                           --no-secmem-warning
--debug-all                       --no-show-notation
--debug-ccid-driver               --no-show-photos
--debug-level                     --no-show-policy-url
--decrypt                         --no-sig-cache
--decrypt-files                   --no-sig-create-check
--default-cert-check-level        --no-sk-comments
--default-cert-expire             --no-strict
--default-cert-level              --notation-data
--default-comment                 --not-dash-escaped
--default-key                     --no-textmode
--default-keyserver-url           --no-throw-keyid
--default-preference-list         --no-throw-keyids
--default-recipient               --no-tty
--default-recipient-self          --no-use-agent
--default-sig-expire              --no-use-embedded-filename
--delete-keys                     --no-utf8-strings
--delete-secret-and-public-keys   --no-verbose
--delete-secret-keys              --no-version
--desig-revoke                    --openpgp
--detach-sign                     --options
--digest-algo                     --output
--disable-ccid                    --override-session-key
--disable-cipher-algo             --passphrase
--disable-dsa2                    --passphrase-fd
--disable-mdc                     --passphrase-file
--disable-pubkey-algo             --passphrase-repeat
--display                         --pcsc-driver
--display-charset                 --personal-cipher-preferences
--dry-run                         --personal-cipher-prefs
--dump-options                    --personal-compress-preferences
--edit-key                        --personal-compress-prefs
--emit-version                    --personal-digest-preferences
--enable-dsa2                     --personal-digest-prefs
--enable-progress-filter          --pgp2
--enable-special-filenames        --pgp6
--enarmor                         --pgp7
--enarmour                        --pgp8
--encrypt                         --photo-viewer
--encrypt-files                   --pipemode
--encrypt-to                      --preserve-permissions
--escape-from-lines               --primary-keyring
--exec-path                       --print-md
--exit-on-status-write-error      --print-mds
--expert                          --quick-random
--export                          --quiet
--export-options                  --reader-port
--export-ownertrust               --rebuild-keydb-caches
--export-secret-keys              --recipient
--export-secret-subkeys           --recv-keys
--fast-import                     --refresh-keys
--fast-list-mode                  --remote-user
--fetch-keys                      --require-backsigs
--fingerprint                     --require-cross-certification
--fixed-list-mode                 --require-secmem
--fix-trustdb                     --rfc1991
--force-mdc                       --rfc2440
--force-ownertrust                --rfc2440-text
--force-v3-sigs                   --rfc4880
--force-v4-certs                  --run-as-shm-coprocess
--for-your-eyes-only              --s2k-cipher-algo
--gen-key                         --s2k-count
--gen-prime                       --s2k-digest-algo
--gen-random                      --s2k-mode
--gen-revoke                      --search-keys
--gnupg                           --secret-keyring
--gpg-agent-info                  --send-keys
--gpgconf-list                    --set-filename
--gpgconf-test                    --set-filesize
--group                           --set-notation
--help                            --set-policy-url
--hidden-encrypt-to               --show-keyring
--hidden-recipient                --show-notation
--homedir                         --show-photos
--honor-http-proxy                --show-policy-url
--ignore-crc-error                --show-session-key
--ignore-mdc-error                --sig-keyserver-url
--ignore-time-conflict            --sign
--ignore-valid-from               --sign-key
--import                          --sig-notation
--import-options                  --sign-with
--import-ownertrust               --sig-policy-url
--interactive                     --simple-sk-checksum
--keyid-format                    --sk-comments
--keyring                         --skip-verify
--keyserver                       --status-fd
--keyserver-options               --status-file
--lc-ctype                        --store
--lc-messages                     --strict
--limit-card-insert-tries         --symmetric
--list-config                     --temp-directory
--list-key                        --textmode
--list-keys                       --throw-keyid
--list-only                       --throw-keyids
--list-options                    --trustdb-name
--list-ownertrust                 --trusted-key
--list-packets                    --trust-model
--list-public-keys                --try-all-secrets
--list-secret-keys                --ttyname
--list-sig                        --ttytype
--list-sigs                       --ungroup
--list-trustdb                    --update-trustdb
--load-extension                  --use-agent
--local-user                      --use-embedded-filename
--lock-multiple                   --user
--lock-never                      --utf8-strings
--lock-once                       --verbose
--logger-fd                       --verify
--logger-file                     --verify-files
--lsign-key                       --verify-options
--mangle-dos-filenames            --version
--marginals-needed                --warranty
--max-cert-depth                  --with-colons
--max-output                      --with-fingerprint
--merge-only                      --with-key-data
--min-cert-level                  --yes
"""
        .split())
    three_hundred_eighteen.append('--export-ownertrust')
    three_hundred_eighteen.append('--import-ownertrust')
    three_hundred_eighteen.append('--pinentry-mode')
    three_hundred_eighteen.append('--allow-loopback-pinentry')
    gnupg_options = frozenset(three_hundred_eighteen)
    return gnupg_options


def convert_sysv_runlevel(level):
    """
    Convert runlevel to systemd target.

    :param level: sys_v runlevel
    :type level: str or int
    :return: systemd target
    :rtype: str
    :raise ValueError: when runlevel is unknown
    """
    runlevel = str(level)
    if runlevel == '0':
        target = 'poweroff.target'
    elif runlevel in ['1', 's', 'single']:
        target = 'rescue.target'
    elif runlevel in ['2', '3', '4']:
        target = 'multi-user.target'
    elif runlevel == '5':
        target = 'graphical.target'
    elif runlevel == '6':
        target = 'reboot.target'
    else:
        raise ValueError('unknown runlevel %s' % level)
    return target


def split_interaction_label(label, delimiter='-'):
    """Splits an interaction label into its component atom labels.

    .. note:: This function is designed to use relative atom references, like
              C-1 or N+1. These are used to reference atoms in previous or
              subsequent residues, respectively.

    Parameters
    ----------
    label: str
        The string identifier for the interaction. ex: A.14N-H
    delimiter: str
        The delimiter used for separating atom references.

    Returns
    -------
    pieces: list of str
        A list of atom label pieces

    Examples
    --------
    >>> split_interaction_label('14N-H')
    ['14N', 'H']
    >>> split_interaction_label('A.14H-13C')
    ['A.14H', '13C']
    >>> split_interaction_label('A.15N-16C-17H')
    ['A.15N', '16C', '17H']
    >>> split_interaction_label('A.14H-13C-N-1')
    ['A.14H', '13C', 'N-1']
    >>> split_interaction_label('A.14N-C-1')
    ['A.14N', 'C-1']
    >>> split_interaction_label('A.N+1-14C')
    ['A.N+1', '14C']
    """
    pieces = label.split(delimiter)
    l = []
    for count, item in enumerate(pieces):
        if item.isdigit() and count > 0:
            l[count - 1] = l[count - 1] + delimiter + item
        else:
            l.append(item)
    return l


@staticmethod
def pretty_duration(hours):
    """Format a duration in days, hours, minutes, seconds."""
    seconds = int(3600 * hours)
    days, seconds = divmod(seconds, 86400)
    hours, seconds = divmod(seconds, 3600)
    minutes, seconds = divmod(seconds, 60)
    if days > 0:
        return '%dd %dh %dm' % (days, hours, minutes)
    if hours > 0:
        return '%dh %dm' % (hours, minutes)
    return '%dm' % minutes


@classmethod
def stop_watching(cls):
    """ Remove all files that are being watched.
        """
    cls.observer.unschedule_all()


def _add_linking_libs(context, call):
    """
    Add the required libs for the callable to allow inlining.
    """
    libs = getattr(call, 'libs', ())
    if libs:
        context.add_linking_libs(libs)


@staticmethod
def format_session(d, intify=True):
    """ Given a array d [1, 2] or [1] it turns it into a dict with keys start, end """
    d = tuple(map(lambda x: int(x) if intify and x.isdigit() else x, filter
        (lambda x: len(x.strip()) > 0, d)))
    end = d[0] if len(d) < 2 else d[1]
    return {'start': d[0], 'end': end}


def unlock(lock):
    """
    Release a previously-acquired lock.

    Argument `lock` should be the return value of a previous
    `gc3libs.utils.lock` call.

    See also: `gc3libs.utils.lock`:func:
    """
    lock.release()


def str_split(cmd):
    """Split a string by whitespace, unless it is already a list."""
    if isinstance(cmd, str):
        return cmd.split()
    return cmd


def is_instance_or_subclass(val, class_):
    """Return True if ``val`` is either a subclass or instance of ``class_``."""
    try:
        return issubclass(val, class_)
    except TypeError:
        return isinstance(val, class_)


def text(axes, gate_pos, wire_pos, textstr, plot_params):
    """
    Draws a text box on the figure.

    Args:
        axes (matplotlib.axes.Axes): axes object
        gate_pos (float): x coordinate of the gate [data units]
        wire_pos (float): y coordinate of the qubit wire
        textstr (str): text of the gate and box
        plot_params (dict): plot parameters
        box (bool): draw the rectangle box if box is True
    """
    return axes.text(gate_pos, wire_pos, textstr, color='k', ha='center',
        va='center', clip_on=True, size=plot_params['fontsize'])


def clean_headers(headers):
    """
    Sanitize a dictionary containing HTTP headers of sensitive values.

    :param headers: The headers to sanitize.
    :type headers: dict
    :returns: A list of headers without sensitive information stripped out.
    :rtype: dict
    """
    cleaned_headers = headers.copy()
    authorization_header = headers.get('Authorization')
    if authorization_header:
        sanitized = '****' + authorization_header[-4:]
        cleaned_headers['Authorization'] = sanitized
    return cleaned_headers


def _membership_to_list_of_communities(membership_vector, size):
    """Convert membership vector to list of lists of vertices in each community

    Parameters
    ----------
    membership_vector : list of int
        community membership i.e. vertex/label `i` is in community `membership_vector[i]`
    size : int
        the number of communities present in the membership vector

    Returns
    -------
    list_of_members : list of lists of int
        list of lists of vertex/label ids in each community per community
    """
    list_of_members = [[] for _ in range(size)]
    for vertex_id, community_id in enumerate(membership_vector):
        list_of_members[community_id].append(vertex_id)
    return list_of_members


def clean_dir(root):
    """remove .* and _* files and directories under root"""
    for x in root.walkdirs('.*', errors='ignore'):
        x.rmtree()
    for x in root.walkdirs('_*', errors='ignore'):
        x.rmtree()
    for x in root.walkfiles('.*', errors='ignore'):
        x.remove()
    for x in root.walkfiles('_*', errors='ignore'):
        x.remove()


@classmethod
def warping_discontinuity(cls, warping_record):
    """Given a warping record returns either True for a discontiuity
        occured or False if a discontinuity did not occur."""
    if warping_record[2] in cls.DISCONTINUITY_TARGET_IDXS:
        return True
    else:
        return False


def unpack(instance: tuple) ->tuple:
    """Return the inside of any ADT instance.

    This function is not meant for general use.
    """
    return tuple.__getitem__(instance, slice(None))


def get_instance_name_to_id_map(instance_info):
    """
    generate instance_name to instance_id map.
    Every instance without a name will be given a key 'unknownx', where x is an incrementing number of instances without a key.
    """
    instance_name_to_id = {}
    unknown_instance_count = 0
    for instance_id in instance_info:
        instance = instance_info[instance_id]
        instance_name = 'unnamed' + str(unknown_instance_count)
        if 'Tags' in instance:
            for tag in instance['Tags']:
                if tag['Key'] == 'Name':
                    instance_name = tag['Value']
        if instance_name == 'unnamed' + str(unknown_instance_count):
            unknown_instance_count = unknown_instance_count + 1
        instance_name_to_id[instance_name] = instance['InstanceId']
    return instance_name_to_id


@classmethod
def unit_regular_asterisk(cls, numVertices):
    """
        Return a :class:`Path` for a unit regular asterisk with the given
        numVertices and radius of 1.0, centered at (0, 0).
        """
    return cls.unit_regular_star(numVertices, 0.0)


def get_connect_timeout():
    """
        get_connect_timeout

        Get the connect timeout (in seconds)

        @return: the connect timeout (in seconds)
        """


def zero_safe_divide(a, b, default_error_value=0.0):
    """Element-wise division that accounts for floating point errors.

    Both invalid floating-point (e.g. 0. / 0.) and divide be zero errors are
    suppressed. Resulting values (NaN and Inf respectively) are replaced with
    `default_error_value`.

    """
    import numpy as np
    with np.errstate(invalid='ignore', divide='ignore'):
        quotient = np.true_divide(a, b)
        bad_value_indices = np.logical_or(np.isnan(quotient), np.isinf(
            quotient))
        quotient[bad_value_indices] = default_error_value
    return quotient


def get_class_name(x):
    """ Parse <class 'class_name'> to get only class_name. """
    s = str(type(x))
    s = s.replace("<class '", '')
    s = s.replace("<type '", '')
    s = s[0:len(s) - 2]
    return s


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

        :param FileRevertType val:
        :rtype: EventType
        """
    return cls('file_revert', val)


def get_short_hash(repo_path):
    """
    Get git short hash
    """
    return "git -C {repo_path} log --pretty=format:'%h' -n 1".format(repo_path
        =repo_path)


def register_blueprints(app, blueprints):
    """Register blueprints."""
    for blueprint in blueprints:
        app.register_blueprint(blueprint)


@staticmethod
def get_indent(line):
    """ Return only the leading whitespace from a line. """
    indent = []
    while line.startswith((' ', '\t')):
        indent.append(line[0])
        line = line[1:]
    return ''.join(indent)


def get_nodes(node_label, data=False):
    """Generate query returning ids of all nodes of the graph.

    Parameters
    ----------
    label
        Label of the nodes to match, default is 'node'
    """
    if data:
        query = (
            'MATCH (n:{}) RETURN n.id as node_id, properties(n) as attrs\n'
            .format(node_label))
    else:
        query = 'MATCH (n:{}) RETURN n.id as node_id\n'.format(node_label)
    return query


@classmethod
def _create(cls, target_class, *args, **kwargs):
    """Create an instance of the model, and save it to the database."""
    from mogwai.models import Edge
    if issubclass(target_class.__class__, Edge):
        assert 'outV' in kwargs and 'inV' in kwargs, 'Edges require in and out Vertices'
        obj = target_class.create(*args, outV=kwargs.get('outV'), inV=
            kwargs.get('inV'), **kwargs)
    else:
        obj = target_class.create(*args, **kwargs)
    return obj


def add_path_delta(path, delta):
    """
    Add delta to pathrent path, with some cleaning by the way.
    :param path: pathrent path
    :param delta: delta
    :return: new path or raises ValueError

    >>> add_path_delta('/a/b/c', 'd/e')
    '/a/b/c/d/e'
    >>> add_path_delta('a/b/c', 'd/e')
    'a/b/c/d/e'
    >>> add_path_delta('a/b/c', '/d/e')
    '/d/e'
    >>> add_path_delta('/a/b/c', '../d/./e')
    '/a/b/d/e'
    >>> add_path_delta('/a/..//c', 'd/e')
    '/c/d/e'
    >>> add_path_delta('////', '')
    '/'
    >>> add_path_delta('a/b', '../..')
    ''
    >>> add_path_delta('/a/b', '../../..')
    Traceback (most recent call last):
        ...
    ValueError: Path delta out of bound: /a/b + ../../..
    """
    if delta.startswith('/'):
        absolute = True
        steps = delta[1:].split('/')
    else:
        absolute = path.startswith('/')
        steps = path.split('/') + delta.split('/')
    parts = []
    for step in steps:
        if step in ('.', ''):
            continue
        elif step == '..':
            try:
                parts.pop()
            except IndexError:
                raise ValueError(u'Path delta out of bound: {} + {}'.format
                    (path, delta))
        else:
            parts.append(step)
    res_path = '/'.join(parts)
    if absolute:
        res_path = '/' + res_path
    return res_path


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 is_match(source, *patterns):
    """
    gets a value indicating that given file name end, matches with any of given patterns.

    :param str source: source file path.

    :param str patterns: file name end pattern. for example it could
                         be `'.py', '.html'`. it will match all file
                         names if no pattern is provided.

    :rtype: bool
    """
    if len(patterns) <= 0:
        return True
    for pattern in patterns:
        if source.lower().endswith(pattern.lower()):
            return True
    return False


def pairwise(seq):
    """
    Pair an iterable, e.g., (1, 2, 3, 4) -> ((1, 2), (2, 3), (3, 4))
    """
    for i in range(0, len(seq) - 1):
        yield seq[i], seq[i + 1]


def linuxize_path(path):
    """
        path: path that uses os.sep, to be replaced with / for compatibility with zipfile
        """
    return '/'.join(path.split('\\'))


def apply_format(value, fmt):
    """Apply a format string to a given value.

    Here we check the formatting of a float. We are *forcing* a
    *maximum length* on the resulting string. This is to avoid problems
    like: '{:7.2f}'.format(12345.7) which returns '12345.70' with a
    length 8 > 7. The intended use of this function is to avoid such
    problems when we are formatting numbers for tables. Here it is done
    by switching to an exponential notation. But note however that this
    will have implications for how many decimal places we can show.

    Parameters
    ----------
    value : float
        The float to format.
    fmt : string
        The format to use.

    Note
    ----
    This function converts numbers to have a fixed length. In some
    cases this may reduce the number of significant digits. Remember
    to also output your numbers without this format in case a specific
    number of significant digits is important!
    """
    maxlen = fmt.split(':')[1].split('.')[0]
    align = ''
    if not maxlen[0].isalnum():
        align = maxlen[0]
        maxlen = maxlen[1:]
    maxlen = int(maxlen)
    str_fmt = fmt.format(value)
    if len(str_fmt) > maxlen:
        if value < 0:
            deci = maxlen - 7
        else:
            deci = maxlen - 6
        new_fmt = '{{:{0}{1}.{2}e}}'.format(align, maxlen, deci)
        return new_fmt.format(value)
    else:
        return str_fmt


@classmethod
def apply(cls, parser, unmarshaller):
    """
        Apply the patch to a ``parser`` and ``unmarshaller`` obtained from
        xmlrpclib, but only if requested and warranted.
        """
    if cls.requested() and cls.warranted():
        return cls.patch_parser(parser, unmarshaller)
    return parser, unmarshaller


@classmethod
def set_commercial(cls):
    """Deprecated after change to LGPLv3 and has no effect."""
    pass


def patch(source, sink):
    """Create a direct link between a source and a sink.

    Implementation::

        sink = sink()
        for value in source():
            sink.send(value)

    .. |patch| replace:: :py:func:`patch`
    """
    sink = sink()
    for v in source():
        try:
            sink.send(v)
        except StopIteration:
            return


@staticmethod
def _get_len_out(di_graph, rule):
    """Get the number of out edges of a rule (more exactly, the non         terminal at its left.

        Parameters
        ----------
        di_graph : DiGraph
            A directed graph
        rule : :class:`~pyformlang.indexed_grammar.ReducedRule`
            The rule
        """
    if rule.left_term in di_graph:
        return len(di_graph[rule.left_term])
    return 0


@staticmethod
def julian_ephemeris_day(jd, deltat):
    """Calculate the Julian Ephemeris Day from the Julian Day and delta-time = (terrestrial time - universal time) in seconds"""
    return jd + deltat / 86400.0


def _get_mpl_ax(plot, axes):
    """
    Returns MPL Axes that contains the `axes`.

    The space of the first DataAxis in axes will be used to determine which
    plot's matplotlib Axes to return.

    Arguments:
    ----------
        plot : MPL_HyperExplorer
            The explorer that contains the navigation and signal plots
        axes : collection of DataAxis
            The axes to infer from.
    """
    if axes[0].navigate:
        ax = plot.navigator_plot.ax
    elif len(axes) == 2 and axes[1].navigate:
        ax = plot.navigator_plot.ax
    else:
        ax = plot.signal_plot.ax
    return ax


def decode_word_three(word, mask):
    """
    The final word of the standard header. Contains
        * trapezoidal energy
        * trace length
        * trace out of range flag
    :param word: The word that we're going to decode.
    :param mask: The mask we'll use to decode that word.
    :return: A dictionary containing the decoded information
    """
    return {'energy': (word & mask.energy()[0]) >> mask.energy()[1],
        'trace_length': (word & mask.trace_length()[0]) >> mask.
        trace_length()[1], 'trace_out_of_range': (word & mask.
        trace_out_of_range()[0]) >> mask.trace_out_of_range()[1]}


def parse_setting_list(list_setting):
    """ This function parse ep list specified from setting file or cmd line

    Input list has following format [1, 5, 10-15] as a string or list of string
    and is expended as a single list [1, 5, 10, 11, 12, 13, 14, 15]

    Args:
        list_setting(str or list): the list from setting file or cmd line.

    Returns: A single list of ep
    """
    if isinstance(list_setting, str):
        list_setting = list_setting.replace('[', '')
        list_setting = list_setting.replace(']', '')
        list_setting = list_setting.split(',')
    single_list = []
    for ep in list_setting:
        if isinstance(ep, int):
            if ep < 0 or ep > 255:
                raise SyntaxError('EP out of bound')
            single_list.append(ep)
            continue
        try:
            ep = int(ep)
            if ep < 0 or ep > 255:
                raise SyntaxError('EP out of bound')
            single_list.append(ep)
            continue
        except ValueError:
            pass
        try:
            ep = ep.replace("'", '')
            lower, upper = ep.split('-')
            lower = int(lower)
            upper = int(upper)
            if lower > upper or lower < 0 or upper > 255:
                raise SyntaxError('Wrong EP range value')
            single_list += list(range(lower, upper + 1))
        except (AttributeError, ValueError):
            raise SyntaxError('Wrong EP range format')
    return single_list


def addClass(klass):
    """Add a class to the HTML element.

        The class must be added to the ``klass`` attribute.
        """


def getExtension(filename, nDots=None):
    """
    Get file extension.

    Inputs:
    -> filename;
    -> nDots: the maximum number of dots extesions should have.
  """
    filename = filename.split('.')
    lParts = len(filename)
    if nDots is None:
        nDots = lParts - 1
    nDots = -nDots
    if nDots <= -lParts:
        nDots = -(lParts - 1)
    if nDots > -1:
        return ''
    return '.'.join(filename[nDots:])


def insert_stock_trading_record(data: dict, stock_account: object):
    """Inserts a stock trading (i.e., buying or selling stocks) records."""
    from finance.models import Asset, deposit
    if data['category1'].startswith('장내'):
        code_suffix = '.KS'
    elif data['category1'].startswith('코스닥'):
        code_suffix = '.KQ'
    else:
        code_suffix = ''
        raise ValueError(
            "code_suffix could not be determined with the category '{}'".
            format(data['category1']))
    code = data['code'] + code_suffix
    asset = Asset.get_by_symbol(code)
    if asset is None:
        raise ValueError("Asset object could not be retrived with code '{}'"
            .format(code))
    return deposit(stock_account, asset, data['quantity'], data['date'])


def merge_m(rec1, rec2):
    """
    Merge M records.

    Parameters
    ----------
    rec1 : sdt.seisan_M
        Seisan M record.
    rec2 : sdt.seisan_M
        Seisan M record.

    Returns
    -------
    rec1 : sdt.seisan_M
        Seisan M record.

    """
    rec1.mrr_mzz = rec2.mrr_mzz
    rec1.mtt_mxx = rec2.mtt_mxx
    rec1.mpp_myy = rec2.mpp_myy
    rec1.mrt_mzx = rec2.mrt_mzx
    rec1.mrp_mzy = rec2.mrp_mzy
    rec1.mtp_mxy = rec2.mtp_mxy
    rec1.reporting_agency2 = rec2.reporting_agency2
    rec1.mt_coordinate_system = rec2.mt_coordinate_system
    rec1.exponential = rec2.exponential
    rec1.scalar_moment = rec2.scalar_moment
    rec1.method_used_2 = rec2.method_used_2
    rec1.quality_2 = rec2.quality_2
    return rec1


def _task_always(_, task):
    """Return true if the task is marked as "always"."""
    return task.always


def getDelNotificationTarget():
    """
        Returns the currently registered 'delete notification' value, or
        None.
        """


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

        :param TeamMergeRequestSentShownToSecondaryTeamDetails val:
        :rtype: EventDetails
        """
    return cls('team_merge_request_sent_shown_to_secondary_team_details', val)


@classmethod
def stopLoading(cls):
    """Force the page stop all navigations and pending resource fetches.
        """
    return cls.build_send_payload('stopLoading', {}), None


def docstr(obj):
    """Return the doc-string for the object."""
    return getattr(obj, '__doc__', '') or ''


def ptp(array, *args, **kwargs):
    """
    Returns the range of values (maximum - minimum).

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


def construct_authorperm(*args):
    """ Create a post identifier from comment/post object or arguments.
    Examples:

        .. code-block:: python

            >>> from dpaygo.utils import construct_authorperm
            >>> print(construct_authorperm('username', 'permlink'))
            @username/permlink
            >>> print(construct_authorperm({'author': 'username', 'permlink': 'permlink'}))
            @username/permlink

    """
    username_prefix = '@'
    if len(args) == 1:
        op = args[0]
        author, permlink = op['author'], op['permlink']
    elif len(args) == 2:
        author, permlink = args
    else:
        raise ValueError('construct_identifier() received unparsable arguments'
            )
    fields = dict(prefix=username_prefix, author=author, permlink=permlink)
    return '{prefix}{author}/{permlink}'.format(**fields)


def renderable(tiddler, environ=None):
    """
    Return ``True`` if the provided :py:class:`tiddler's
    <tiddlyweb.model.tiddler.Tiddler>` ``type`` is one that can be
    rendered to HTML by the :py:mod:`wikitext <tiddlyweb.wikitext>`
    rendering subsystem.
    """
    if not environ:
        environ = {}
    return (not tiddler.type or tiddler.type == 'None' or tiddler.type in
        environ.get('tiddlyweb.config', {}).get('wikitext.type_render_map', [])
        )


def partition(partition):
    """Validate a partition - used by blackboxes and coarse grains."""
    nodes = set()
    for part in partition:
        for node in part:
            if node in nodes:
                raise ValueError(
                    'Micro-element {} may not be partitioned into multiple macro-elements'
                    .format(node))
            nodes.add(node)


def create_path(segments):
    """Builds a path from a list of segments.

    :param stack: a list of segments
    :return: a path
    """
    return '/' + '/'.join(segments)


def get_a(html, find=''):
    """Finds all the 'a' tags with find in their href"""
    links = []
    for a in html.find_all('a'):
        if a.get('href').find(find) != -1:
            links.append(a)
    return links


@staticmethod
def equals(text, expression):
    """Text equivalence test"""
    if isinstance(expression, str):
        return text == expression
    else:
        return text in expression


def prtOptions():
    """Print a list of command-line options and arguments."""
    print('The command-line options are:')
    print('  --version (print the version number and exit)')
    print('  -r (print the full version string and exit)')
    print('  -v (verbose)')
    print('  -t (print timestamps)')
    print('  -s (save temporary files)')
    print('')
    print('Following the options, list the input flt file names and')
    print('  the associated raw (or calibrated) wavecal file names.')


def complexify_data(data):
    """
    Complexify data packed real, imag.
    """
    return data[(...), ::2] + data[(...), 1::2] * 1.0j


def get_fs(module, blade):
    """Return Filesystem or None"""
    fs = []
    fs.append(module.params['name'])
    try:
        res = blade.file_systems.list_file_systems(names=fs)
        return res.items[0]
    except Exception:
        return None


@classmethod
def geom_col_name(cls):
    """
        Returns the name of the metadata column used to store the
        the feature geometry column.
        """
    return 'f_geometry_column'


def gapbaup(f):
    """Determine whether or not there has been a breakaway gap up.

    Parameters
    ----------
    f : pandas.DataFrame
        Dataframe with columns ``open`` and ``high``.

    Returns
    -------
    new_column : pandas.Series (bool)
        The array containing the new feature.

    References
    ----------
    *A breakaway gap represents a gap in the movement of a stock price
    supported by levels of high volume* [IP_BAGAP]_.

    """
    new_column = f['open'] > f['high'].shift(1)
    return new_column


def prepare_lookup_value(key, value):
    """
    Returns a lookup value prepared to be used in queryset filtering.
    """
    if key.endswith('__in'):
        value = value.split(',')
    if key.endswith('__isnull'):
        if value.lower() in ('', 'false', '0'):
            value = False
        else:
            value = True
    return value


def channels_collapse(all: bool=True):
    """Collapse (i.e. close) all selected expandable animation channels

    :param all: All, Collapse all channels (not just selected ones)
    :type all: bool
    """
    pass


def is_user(user):
    """Return ``True`` if passed object is User and ``False`` otherwise."""
    return type(user).__name__ == 'User'


def __call__(request):
    """ Return an ISession object """


def GetSHCoeff(pId, parmDict, SHkeys):
    """default doc string

    :param type name: description

    :returns: type name: description

    """
    SHCoeff = {}
    for shkey in SHkeys:
        shname = str(pId) + '::' + shkey
        SHCoeff[shkey] = parmDict[shname]
    return SHCoeff


@staticmethod
def inherit_dict(base, namespace, attr_name, inherit=lambda k, v: True):
    """
        Perform inheritance of dictionaries.  Returns a list of key
        and value pairs for values that were inherited, for
        post-processing.

        :param base: The base class being considered; see
                     ``iter_bases()``.
        :param namespace: The dictionary of the new class being built.
        :param attr_name: The name of the attribute containing the
                          dictionary to be inherited.
        :param inherit: Filtering function to determine if a given
                        item should be inherited.  If ``False`` or
                        ``None``, item will not be added, but will be
                        included in the returned items.  If a
                        function, the function will be called with the
                        key and value, and the item will be added and
                        included in the items list only if the
                        function returns ``True``.  By default, all
                        items are added and included in the items
                        list.
        """
    items = []
    base_dict = getattr(base, attr_name, {})
    new_dict = namespace.setdefault(attr_name, {})
    for key, value in base_dict.items():
        if key in new_dict or inherit and not inherit(key, value):
            continue
        if inherit:
            new_dict[key] = value
        items.append((key, value))
    return items


def write_output(init_dict, df):
    """The function converts the simulated variables to a panda data frame and saves the
    data in a txt and a pickle file.
    """
    source = init_dict['SIMULATION']['source']
    df.to_pickle(source + '.grmpy.pkl')
    with open(source + '.grmpy.txt', 'w') as file_:
        df.to_string(file_, index=False, na_rep='.', col_space=15, justify=
            'left')
    return df


def make_cgview_tab_file(args, seqlen, cgview_entries):
    """
    http://wishart.biology.ualberta.ca/cgview/tab_input.html
    """
    results = []
    header = ['#{}'.format(args.name), '%{}'.format(seqlen + 1),
        '!strand\tslot\tstart\tstop\ttype\tlabel\tmouseover\thyperlink']
    results.extend(header)
    results.extend(cgview_entries)
    return results


def p_enum_item(p):
    """enum_item : IDENTIFIER '=' INTCONSTANT
                 | IDENTIFIER
                 |"""
    if len(p) == 4:
        p[0] = [p[1], p[3]]
    elif len(p) == 2:
        p[0] = [p[1], None]


def convert2boolean(arg):
    """convert2boolean is used for environmental variables that must be
    returned as boolean"""
    if not isinstance(arg, bool):
        return arg.lower() in ('yes', 'true', 't', '1', 'y')
    return arg


def write_data(db: dict, record_id: int, data_name: str, data):
    """Write data for a given record_id
    """
    if not db['db'].keys():
        db['db'][str(record_id)] = {}
    elif str(record_id) not in db['db']:
        db['db'][str(record_id)] = {}
    if not db['db'][str(record_id)].keys():
        db['db'][str(record_id)] = {data_name: data}
    elif db['writeback']:
        db['db'][str(record_id)][data_name] = data
    else:
        tmp_dict = db['db'][str(record_id)]
        tmp_dict[data_name] = data
        db['db'][str(record_id)] = tmp_dict


def cleanup_string(path_name):
    """Function to remove some characters that don't play nicely on Windows machines filesystem"""
    path_name = path_name.replace('/', '-')
    path_name = path_name.replace(' :', ' -')
    path_name = path_name.replace(': ', ' - ')
    path_name = path_name.replace(':', '-')
    path_name = path_name.replace('?', '')
    return path_name