@classmethod
def from_dict(cls, d):
    """
        Instances the object from a dictionary representation.

        :param d: Dictionary containing a serialised object.
        :return: An object instance.
        """
    if not cls.is_compatible(d):
        raise ValueError(
            'The dictionary does not contain a serialised scalar class.')
    return cls(d['description'])


def _get_initializer_tensors(onnx_model_proto):
    """Create an iterator of initializer tensors from ONNX model."""
    for initializer in onnx_model_proto.graph.initializer:
        yield initializer


def expand_var(s, var, expansions):
    """Expands a template string of the form ${VAR} with each entry of the
    expansions array, then join them in using the same scheme as the PATH
    variable. For example, given:
    s = ${ABC}/stuff
    var = ABC
    expansions = ['a', 'b', 'c'],
    then expand_sub would return 'a/stuff:b/stuff:c/stuff'."""
    results = []
    for expansion in expansions:
        result = s.replace('${%s}' % var, expansion)
        if result not in results:
            results.append(result)
    return ':'.join(results)


def target_move_up():
    """Move particle target up in the list

    """
    pass


def FormatClassToPython(i):
    """
    Transform a typed class name into a form which can be used as a python
    attribute

    example::

        >>> FormatClassToPython('Lfoo/bar/foo/Barfoo$InnerClass;')
        'Lfoo_bar_foo_Barfoo_InnerClass'

    :param i: classname to transform
    :rtype: str
    """
    i = i[:-1]
    i = i.replace('/', '_')
    i = i.replace('$', '_')
    return i


@staticmethod
def strategy(data, params):
    """
        Calculate boundaries of geometry object
        """
    return data


def _sanity_check(name, package, level):
    """Verify arguments are "sane"."""
    if not isinstance(name, str):
        raise TypeError('module name must be str, not {}'.format(type(name)))
    if level < 0:
        raise ValueError('level must be >= 0')
    if level > 0:
        if not isinstance(package, str):
            raise TypeError('__package__ not set to a string')
        elif not package:
            raise ImportError(
                'attempted relative import with no known parent package')
    if not name and level == 0:
        raise ValueError('Empty module name')


def formula_2002_01_01(foyer_fiscal, period, parameters):
    """
        Renvoie la liste des charges déductibles avant rbg_int pour 2002
        """
    pensions_alimentaires_deduites = foyer_fiscal(
        'pensions_alimentaires_deduites', period)
    cd_acc75a = foyer_fiscal('cd_acc75a', period)
    pertes_capital_societes_nouvelles = foyer_fiscal(
        'pertes_capital_societes_nouvelles', period)
    cd_deddiv = foyer_fiscal('cd_deddiv', period)
    cd_doment = foyer_fiscal('cd_doment', period)
    niches1 = (pensions_alimentaires_deduites + cd_acc75a +
        pertes_capital_societes_nouvelles + cd_deddiv + cd_doment)
    return niches1


@classmethod
def get_full_info(cls):
    """Returns a sequence of all available SQL Modes

        This class method returns a tuple containing all SQL Mode names. The
        names will be alphabetically sorted.

        Returns a tuple.
        """
    res = []
    for key in vars(cls).keys():
        if not key.startswith('_') and not hasattr(getattr(cls, key),
            '__call__'):
            res.append(key)
    return tuple(sorted(res))


@classmethod
def _get_column(cls, name):
    """
        Returns the column matching the given name, raising a key error if
        it doesn't exist

        :param name: the name of the column to return
        :rtype: Column
        """
    return cls._columns[name]


def mark_freestyle_edge(clear: bool=False):
    """(Un)mark selected edges as Freestyle feature edges

    :param clear: Clear
    :type clear: bool
    """
    pass


@classmethod
def dynamorm_validate(cls, obj, partial=False, native=False):
    """Given a dictionary representing a blob from dynamo, this method will validate the blob given the desired
        validation library.

        If partial is true then the underlying validation library should allow for partial objects.

        If native is true then the underlying validation library should return a dictionary of native python values
        (i.e. datetime.datetime), otherwise it should return a dictionary of primitive values (i.e. a string
        representation of a date time value).

        On validation failure, this should raise ``dynamorm.exc.ValidationError``.
        """
    raise NotImplementedError('{0} class must implement dynamallow_validate'
        .format(cls.__name__))


def get_reference_by_uuid(yaml_dict, id_):
    """ Finds reference directory by uuid """
    keys = yaml_dict.keys()
    for k in keys:
        levels = yaml_dict[k]['levels']
        if 'references' in levels.keys():
            ref = levels['references']
            for i, entry in enumerate(ref):
                if ref[i]['uuid'] == id_:
                    loc = ref[i]['location']
    return loc


@staticmethod
def get_testcase_property(test) ->tuple:
    """
            接受一个test, 并返回一个test的class_name, method_name, method_doc属性
        :param test:
        :return: (class_name, method_name, method_doc) -> tuple
        """
    class_name = test.__class__.__qualname__
    method_name = test.__dict__['_testMethodName']
    method_doc = test.__dict__['_testMethodDoc']
    return class_name, method_name, method_doc


def get_user_agent(module):
    """Retrieve a user-agent to send with LinodeClient requests."""
    try:
        from ansible.module_utils.ansible_release import __version__ as ansible_version
    except ImportError:
        ansible_version = 'unknown'
    return 'Ansible-%s/%s' % (module, ansible_version)


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 legendre_symbol(a, p):
    """ Compute the Legendre symbol a|p using
    Euler's criterion. p is a prime, a is
    relatively prime to p (if p divides
    a, then a|p = 0)

    Returns 1 if a has a square root modulo
    p, -1 otherwise.
    """
    ls = pow(a, (p - 1) / 2, p)
    return -1 if ls == p - 1 else ls


def use(query, sid):
    """
    Shortcut for calling "use" on the query-object
    """
    query.command('use', params={'sid': sid})


def cocktail_shaker_sort(collection):
    """
    鸡尾酒排序法(即:定向冒泡排序法)
    :param collection:
    :return:
    """
    for i in range(len(collection) - 1, 0, -1):
        swapped = False
        for j in range(i, 0, -1):
            if collection[j] < collection[j - 1]:
                collection[j], collection[j - 1] = collection[j - 1
                    ], collection[j]
                swapped = True
        for j in range(i):
            if collection[j] > collection[j + 1]:
                collection[j], collection[j + 1] = collection[j + 1
                    ], collection[j]
                swapped = True
        if not swapped:
            return collection


def add_rows(me, n):
    """add_rows with transparent index mapping."""
    return me._rowmap.add(n, me._add_rows)


def count_class(classes, class_label):
    """Increment the _count in classes for a particular class,
    given its canonical label."""
    if not class_label in classes:
        raise Exception('Unknown Class: %s' % class_label)
    if '_count' not in classes[class_label]:
        classes[class_label]['_count'] = 0
    classes[class_label]['_count'] += 1


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

        :param SecondaryEmailVerifiedDetails val:
        :rtype: EventDetails
        """
    return cls('secondary_email_verified_details', val)


@staticmethod
def never_trigger(dt):
    """
        A should_trigger implementation that will never trigger.
        """
    return False


def getRules(event, bubbled=False):
    """Get all enabled rules registered for the given event and
        assigned to this context. If bubbled is True, only rules that are
        bubbleable will be returned.
        """


def snap_cursor_to_center():
    """Snap cursor to the Center

    """
    pass


def DeadEndMetabolites(model, nodeadendmodel=None):
    """ iteratively identify peripheral metabolites """
    if nodeadendmodel == None:
        nodeadendmodel = model.NoDeadEndModel()
    return list(set(model.Metabolites()).difference(nodeadendmodel.
        Metabolites()))


def merge_field(left, right, field):
    """Merge field of right instance to left one."""
    left_field = getattr(left, field)
    if not left_field:
        setattr(left, field, getattr(right, field))


@classmethod
def user_exists(cls, *args, **kwargs):
    """
        Return True/False if a User instance exists with the given arguments.
        Arguments are directly passed to filter() manager method.
        """
    raise NotImplementedError('Implement in subclass')


@staticmethod
def debug(message):
    """Log a debug level log message."""
    print('DEBUG: ' + message)


def _try_f2i(x):
    """If possible, convert float to int without rounding.
    Used for log base: if not used, base for log scale can be "10.0" (and then
    printed as such  by pgfplots).
    """
    return int(x) if int(x) == x else x


def instance_name(instance):
    """Shortcut to get instance name."""
    return getattr(instance, 'OS-EXT-SRV-ATTR:instance_name', None)


def check_game_mode(game_mode_name):
    """Validation for a game mode script.

    Check if the game_mode is not a default one ('ctf' or 'tc').

    Args:
        game_mode_name: The game mode name to be checked

    Return:
        bool: True if the game mode is not a default one, False otherwise
    """
    return game_mode_name not in ('ctf', 'tc')


@staticmethod
def amIGoingToFail():
    """
        Just stop trying
        :return: Failure, regret and  tears.
        """
    return 'Probably'


def list_lzop(archive, compression, cmd, verbosity, interactive):
    """List a LZOP archive."""
    cmdlist = [cmd, '--list']
    if verbosity > 1:
        cmdlist.append('--verbose')
    cmdlist.extend(['--', archive])
    return cmdlist


def iter_dates(start, end, period):
    """Yield dates from `start` to `end` with step equalt to `period`."""
    current = start
    while current <= end:
        yield current
        current += period


def dynamic_topology_toggle():
    """Dynamic topology alters the mesh topology while sculpting

    """
    pass


def stripascii(string):
    """Return string truncated at last byte that is 7-bit ASCII.

    Clean NULL separated and terminated TIFF strings.

    >>> stripascii(b'string\\x00string\\n\\x01\\x00')
    b'string\\x00string\\n'
    >>> stripascii(b'\\x00')
    b''

    """
    i = len(string)
    while i:
        i -= 1
        if 8 < string[i] < 127:
            break
    else:
        i = -1
    return string[:i + 1]


def viscosity_sutherland(temp):
    """compute visocity as in sutherland

    Args:
        temp (TYPE): Description

    Returns:
        TYPE: Description

    """
    alpha = 1.716e-05
    beta = 1.5
    t_0 = 273.15
    t_1 = 110.4
    mu_wall = alpha * (temp / t_0) ** beta * (t_0 + t_1) / (temp + t_1)
    return mu_wall


def manage_addGroup(newgroup_name, newgroup_type='groupOfUniqueNames',
    REQUEST=None):
    """ Add a new group under the group record base of type
        ``newgroup_type``. If REQUEST is not None a MessageDialog screen will
        be returned. The group_name argument forms the new group CN while the
        full DN will be formed by combining this new CN with the group base DN.

        Since a group record cannot be empty, meaning there must be at least
        a single uniqueMember element in it, the DN given as the binduid in
        the LDAPUserFolder configuration is inserted.

        Permission: *Manage users*
        """


@classmethod
def get_last(cls, **kwargs):
    """
        Gets the last item from a collection, returning a DatabaseObject.
        This is primarily for collections that use random_id's, as mongo's id's
        are in alphachronological order (alphabetical order of time), and this
        will return the most recent item in that case.

        @param **kwargs: forwarded to cls.__init__
        """
    try:
        return cls(page_size=1, ascending=False, **kwargs)[0]
    except IndexError:
        return None


def check_multiplicity(par, values, bundle_length):
    """
    Check the number of values for the parameter against bundle length,
    depending on whether the parameter is required or multiple.
    """
    name = par['name']
    required = par['required']
    multiple = par['multiple']
    if required and len(values) == 0:
        raise ValueError('need a value for %s' % name)
    if not multiple and len(values) not in [0, 1, bundle_length]:
        raise ValueError('wrong number of values for %s' % name)


def create_cache_cluster(CacheClusterId=None, ReplicationGroupId=None,
    AZMode=None, PreferredAvailabilityZone=None, PreferredAvailabilityZones
    =None, NumCacheNodes=None, CacheNodeType=None, Engine=None,
    EngineVersion=None, CacheParameterGroupName=None, CacheSubnetGroupName=
    None, CacheSecurityGroupNames=None, SecurityGroupIds=None, Tags=None,
    SnapshotArns=None, SnapshotName=None, PreferredMaintenanceWindow=None,
    Port=None, NotificationTopicArn=None, AutoMinorVersionUpgrade=None,
    SnapshotRetentionLimit=None, SnapshotWindow=None, AuthToken=None):
    """
    Creates a cache cluster. All nodes in the cache cluster run the same protocol-compliant cache engine software, either Memcached or Redis.
    See also: AWS API Documentation

    :example: response = client.create_cache_cluster(
        CacheClusterId='string',
        ReplicationGroupId='string',
        AZMode='single-az'|'cross-az',
        PreferredAvailabilityZone='string',
        PreferredAvailabilityZones=[
            'string',
        ],
        NumCacheNodes=123,
        CacheNodeType='string',
        Engine='string',
        EngineVersion='string',
        CacheParameterGroupName='string',
        CacheSubnetGroupName='string',
        CacheSecurityGroupNames=[
            'string',
        ],
        SecurityGroupIds=[
            'string',
        ],
        Tags=[
            {
                'Key': 'string',
                'Value': 'string'
            },
        ],
        SnapshotArns=[
            'string',
        ],
        SnapshotName='string',
        PreferredMaintenanceWindow='string',
        Port=123,
        NotificationTopicArn='string',
        AutoMinorVersionUpgrade=True|False,
        SnapshotRetentionLimit=123,
        SnapshotWindow='string',
        AuthToken='string'
    )

    :type CacheClusterId: string
    :param CacheClusterId: [REQUIRED]
            The node group (shard) identifier. This parameter is stored as a lowercase string.
            Constraints:
            A name must contain from 1 to 20 alphanumeric characters or hyphens.
            The first character must be a letter.
            A name cannot end with a hyphen or contain two consecutive hyphens.

    :type ReplicationGroupId: string
    :param ReplicationGroupId:
            Warning
            Due to current limitations on Redis (cluster mode disabled), this operation or parameter is not supported on Redis (cluster mode enabled) replication groups.
            The ID of the replication group to which this cache cluster should belong. If this parameter is specified, the cache cluster is added to the specified replication group as a read replica; otherwise, the cache cluster is a standalone primary that is not part of any replication group.
            If the specified replication group is Multi-AZ enabled and the Availability Zone is not specified, the cache cluster is created in Availability Zones that provide the best spread of read replicas across Availability Zones.
            Note
            This parameter is only valid if the Engine parameter is redis .

    :type AZMode: string
    :param AZMode: Specifies whether the nodes in this Memcached cluster are created in a single Availability Zone or created across multiple Availability Zones in the cluster's region.
            This parameter is only supported for Memcached cache clusters.
            If the AZMode and PreferredAvailabilityZones are not specified, ElastiCache assumes single-az mode.

    :type PreferredAvailabilityZone: string
    :param PreferredAvailabilityZone: The EC2 Availability Zone in which the cache cluster is created.
            All nodes belonging to this Memcached cache cluster are placed in the preferred Availability Zone. If you want to create your nodes across multiple Availability Zones, use PreferredAvailabilityZones .
            Default: System chosen Availability Zone.

    :type PreferredAvailabilityZones: list
    :param PreferredAvailabilityZones: A list of the Availability Zones in which cache nodes are created. The order of the zones in the list is not important.
            This option is only supported on Memcached.
            Note
            If you are creating your cache cluster in an Amazon VPC (recommended) you can only locate nodes in Availability Zones that are associated with the subnets in the selected subnet group.
            The number of Availability Zones listed must equal the value of NumCacheNodes .
            If you want all the nodes in the same Availability Zone, use PreferredAvailabilityZone instead, or repeat the Availability Zone multiple times in the list.
            Default: System chosen Availability Zones.
            (string) --

    :type NumCacheNodes: integer
    :param NumCacheNodes: The initial number of cache nodes that the cache cluster has.
            For clusters running Redis, this value must be 1. For clusters running Memcached, this value must be between 1 and 20.
            If you need more than 20 nodes for your Memcached cluster, please fill out the ElastiCache Limit Increase Request form at http://aws.amazon.com/contact-us/elasticache-node-limit-request/ .

    :type CacheNodeType: string
    :param CacheNodeType: The compute and memory capacity of the nodes in the node group (shard).
            Valid node types are as follows:
            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

            Notes:
            All T2 instances are created in an Amazon Virtual Private Cloud (Amazon VPC).
            Redis backup/restore is not supported for Redis (cluster mode disabled) T1 and T2 instances. Backup/restore is supported on Redis (cluster mode enabled) T2 instances.
            Redis Append-only files (AOF) functionality is not supported for T1 or T2 instances.
            For a complete listing of node types and specifications, see Amazon ElastiCache Product Features and Details and either Cache Node Type-Specific Parameters for Memcached or Cache Node Type-Specific Parameters for Redis .

    :type Engine: string
    :param Engine: The name of the cache engine to be used for this cache cluster.
            Valid values for this parameter are: memcached | redis

    :type EngineVersion: string
    :param EngineVersion: The version number of the cache engine to be used for this cache cluster. To view the supported cache engine versions, use the DescribeCacheEngineVersions operation.
            Important: You can upgrade to a newer engine version (see Selecting a Cache Engine and Version ), but you cannot downgrade to an earlier engine version. If you want to use an earlier engine version, you must delete the existing cache cluster or replication group and create it anew with the earlier engine version.

    :type CacheParameterGroupName: string
    :param CacheParameterGroupName: The name of the parameter group to associate with this cache cluster. If this argument is omitted, the default parameter group for the specified engine is used. You cannot use any parameter group which has cluster-enabled='yes' when creating a cluster.

    :type CacheSubnetGroupName: string
    :param CacheSubnetGroupName: The name of the subnet group to be used for the cache cluster.
            Use this parameter only when you are creating a cache cluster in an Amazon Virtual Private Cloud (Amazon VPC).
            Warning
            If you're going to launch your cluster in an Amazon VPC, you need to create a subnet group before you start creating a cluster. For more information, see Subnets and Subnet Groups .

    :type CacheSecurityGroupNames: list
    :param CacheSecurityGroupNames: A list of security group names to associate with this cache cluster.
            Use this parameter only when you are creating a cache cluster outside of an Amazon Virtual Private Cloud (Amazon VPC).
            (string) --

    :type SecurityGroupIds: list
    :param SecurityGroupIds: One or more VPC security groups associated with the cache cluster.
            Use this parameter only when you are creating a cache cluster in an Amazon Virtual Private Cloud (Amazon VPC).
            (string) --

    :type Tags: list
    :param Tags: A list of cost allocation tags to be added to this resource. A tag is a key-value pair. A tag key must be accompanied by a tag value.
            (dict) --A cost allocation Tag that can be added to an ElastiCache cluster or replication group. Tags are composed of a Key/Value pair. A tag with a null Value is permitted.
            Key (string) --The key for the tag. May not be null.
            Value (string) --The tag's value. May be null.

    :type SnapshotArns: list
    :param SnapshotArns: A single-element string list containing an Amazon Resource Name (ARN) that uniquely identifies a Redis RDB snapshot file stored in Amazon S3. The snapshot file is used to populate the node group (shard). The Amazon S3 object name in the ARN cannot contain any commas.
            Note
            This parameter is only valid if the Engine parameter is redis .
            Example of an Amazon S3 ARN: arn:aws:s3:::my_bucket/snapshot1.rdb
            (string) --

    :type SnapshotName: string
    :param SnapshotName: The name of a Redis snapshot from which to restore data into the new node group (shard). The snapshot status changes to restoring while the new node group (shard) is being created.
            Note
            This parameter is only valid if the Engine parameter is redis .

    :type PreferredMaintenanceWindow: string
    :param PreferredMaintenanceWindow: Specifies the weekly time range during which maintenance on the cache cluster is performed. It is specified as a range in the format ddd:hh24:mi-ddd:hh24:mi (24H Clock UTC). The minimum maintenance window is a 60 minute period. Valid values for ddd are:
            Specifies the weekly time range during which maintenance on the cluster is performed. It is specified as a range in the format ddd:hh24:mi-ddd:hh24:mi (24H Clock UTC). The minimum maintenance window is a 60 minute period.
            Valid values for ddd are:
            sun
            mon
            tue
            wed
            thu
            fri
            sat
            Example: sun:23:00-mon:01:30

    :type Port: integer
    :param Port: The port number on which each of the cache nodes accepts connections.

    :type NotificationTopicArn: string
    :param NotificationTopicArn: The Amazon Resource Name (ARN) of the Amazon Simple Notification Service (SNS) topic to which notifications are sent.
            Note
            The Amazon SNS topic owner must be the same as the cache cluster owner.

    :type AutoMinorVersionUpgrade: boolean
    :param AutoMinorVersionUpgrade: This parameter is currently disabled.

    :type SnapshotRetentionLimit: integer
    :param SnapshotRetentionLimit: The number of days for which ElastiCache retains automatic snapshots before deleting them. For example, if you set SnapshotRetentionLimit to 5, a snapshot taken today is retained for 5 days before being deleted.
            Note
            This parameter is only valid if the Engine parameter is redis .
            Default: 0 (i.e., automatic backups are disabled for this cache cluster).

    :type SnapshotWindow: string
    :param SnapshotWindow: The daily time range (in UTC) during which ElastiCache begins taking a daily snapshot of your node group (shard).
            Example: 05:00-09:00
            If you do not specify this parameter, ElastiCache automatically chooses an appropriate time range.
            Note: This parameter is only valid if the Engine parameter is redis .

    :type AuthToken: string
    :param AuthToken:
            Reserved parameter. The password used to access a password protected server.
            Password constraints:
            Must be only printable ASCII characters.
            Must be at least 16 characters and no more than 128 characters in length.
            Cannot contain any of the following characters: '/', ''', or '@'.
            For more information, see AUTH password at Redis.

    :rtype: dict
    :return: {
        'CacheCluster': {
            'CacheClusterId': 'string',
            'ConfigurationEndpoint': {
                'Address': 'string',
                'Port': 123
            },
            'ClientDownloadLandingPage': 'string',
            'CacheNodeType': 'string',
            'Engine': 'string',
            'EngineVersion': 'string',
            'CacheClusterStatus': 'string',
            'NumCacheNodes': 123,
            'PreferredAvailabilityZone': 'string',
            'CacheClusterCreateTime': datetime(2015, 1, 1),
            'PreferredMaintenanceWindow': 'string',
            'PendingModifiedValues': {
                'NumCacheNodes': 123,
                'CacheNodeIdsToRemove': [
                    'string',
                ],
                'EngineVersion': 'string',
                'CacheNodeType': 'string'
            },
            'NotificationConfiguration': {
                'TopicArn': 'string',
                'TopicStatus': 'string'
            },
            'CacheSecurityGroups': [
                {
                    'CacheSecurityGroupName': 'string',
                    'Status': 'string'
                },
            ],
            'CacheParameterGroup': {
                'CacheParameterGroupName': 'string',
                'ParameterApplyStatus': 'string',
                'CacheNodeIdsToReboot': [
                    'string',
                ]
            },
            'CacheSubnetGroupName': 'string',
            'CacheNodes': [
                {
                    'CacheNodeId': 'string',
                    'CacheNodeStatus': 'string',
                    'CacheNodeCreateTime': datetime(2015, 1, 1),
                    'Endpoint': {
                        'Address': 'string',
                        'Port': 123
                    },
                    'ParameterGroupStatus': 'string',
                    'SourceCacheNodeId': 'string',
                    'CustomerAvailabilityZone': 'string'
                },
            ],
            'AutoMinorVersionUpgrade': True|False,
            'SecurityGroups': [
                {
                    'SecurityGroupId': 'string',
                    'Status': 'string'
                },
            ],
            'ReplicationGroupId': 'string',
            'SnapshotRetentionLimit': 123,
            'SnapshotWindow': '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 is_list(obj):
    """
        from elist.elist import *
        is_list([1,2,3])
        is_list(200)
    """
    if type(obj) == type([]):
        return True
    else:
        return False


def evaluate_using_acc(model, X_test, y_test, batch_size):
    """ Evaluation function using accuracy.

    # Arguments:
        model: Model to be evaluated.
        X_test: Inputs of the testing set.
        y_test: Outputs of the testing set.
        batch_size: Batch size.

    # Returns:
        Accuracy of the given model.
    """
    _, acc = model.evaluate(X_test, y_test, batch_size=batch_size, verbose=0)
    return acc


def compute_auto_interval(interval, beg, end):
    """
    Compute the automatic interval
    """
    if interval == 'auto':
        delta = end - beg
        if delta.days >= 2 * 365:
            return '30d'
        elif delta.days >= 365:
            return '1w'
        elif delta.days >= 48:
            return '1d'
        elif delta.days >= 12:
            return '12h'
        elif delta.days >= 4:
            return '3h'
        elif delta.total_seconds() >= 36 * 3600:
            return '1h'
        elif delta.total_seconds() >= 16 * 3600:
            return '30m'
        elif delta.total_seconds() >= 8 * 3600:
            return '10m'
        elif delta.total_seconds() >= 2 * 3600:
            return '5m'
        elif delta.total_seconds() >= 36 * 60:
            return '1m'
        elif delta.total_seconds() >= 12 * 60:
            return '30s'
        elif delta.total_seconds() >= 6 * 60:
            return '10s'
        elif delta.total_seconds() >= 4 * 60:
            return '5s'
        else:
            return '1s'
    return f'1{interval}'


@classmethod
def set_default_renderer(cls, renderer):
    """ Set the callable that will act as a default renderer for
        instances of the associated class when no ``renderer``
        argument is provided to the class' constructor.  Useful when
        you'd like to use an alternate templating system.

        Calling this method resets the :term:`default renderer`.
        """
    cls.default_renderer = staticmethod(renderer)


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

        :param FileLockingLockStatusChangedDetails val:
        :rtype: EventDetails
        """
    return cls('file_locking_lock_status_changed_details', val)


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


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


def htmlunquote(text):
    """
    Decodes `text` that's HTML quoted.

        >>> htmlunquote(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 _isNULL(obj):
    """Prevent asizeof(all=True, ...) crash.

       Sizing gc.get_objects() crashes in Pythonista3 with
       Python 3.5.1 on iOS due to 1-tuple (<Null>,) object,
       see <http://forum.omz-software.com/user/mrjean1>.
    """
    return isinstance(obj, tuple) and len(obj) == 1 and repr(obj
        ) == '(<NULL>,)'


@classmethod
def get_latest_valid(cls, hosts):
    """ Retrieve valid certificates by fqdn. """
    certs = cls.list({'status': 'valid', 'items_per_page': 500})
    possible = None
    if not isinstance(hosts, (tuple, list)):
        hosts = [hosts]
    for cert in certs:
        cert_hosts = set([cert['cn']] + cert['altnames'])
        if len(set(hosts) - cert_hosts) == 0:
            if possible and possible['date_end'] < cert['date_end'
                ] or not possible:
                possible = cert
    return possible


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 unique():
    """
    Generates a new pseudo unique identifier generated in the
    most efficient way by incrementing a global counter.

    This operation should be used for observer based contexts
    and not outside it, in order to avoid possible problems.

    :rtype: int
    :return: The pseudo unique integer identifier generated by
    incrementing the global counter value.
    """
    global COUNTER
    COUNTER += 1
    return COUNTER


def status_command(api, command, **selections):
    """ Launch testbed status commands

    :param api: API Rest api object
    :param command: command that should be run
    :param **selections: other selections (site, archi, state)
    """
    assert command in ('sites', 'nodes', 'nodes-ids', 'experiments')
    if command == 'experiments':
        result = api.get_running_experiments()
    elif command == 'sites':
        result = api.get_sites_details()
    elif command == 'nodes':
        result = api.get_nodes(**selections)
    else:
        result = api.get_nodes(list_id=True, **selections)
    return result


def decode_text_from_webwx(text):
    """
    解码从 Web 微信获得到的中文乱码

    :param text: 从 Web 微信获得到的中文乱码
    """
    if isinstance(text, str):
        try:
            text = text.encode('raw_unicode_escape').decode()
        except UnicodeDecodeError:
            pass
    return text


def select_interior_faces():
    """Select faces where all edges have more than 2 face users

    """
    pass


def filePathToSafeString(filePath):
    """
    Returns string representation of a given filepath safe for a single filename usage

    >>> filePathToSafeString('C:/Windows/system32')
    'C__Windows_system32'
    """
    retVal = filePath.replace('/', '_').replace('\\', '_')
    retVal = retVal.replace(' ', '_').replace(':', '_')
    return retVal


def translate_crops(crop_tuples, translate_tuple):
    """Translate crop tuples to be over the image are, i.e. left top at (0,0)."""
    crop_tuples_translated = []
    for crop_tuple in crop_tuples:
        crop_tuples_translated.append((crop_tuple[0] - translate_tuple[0],
            crop_tuple[1] - translate_tuple[1], crop_tuple[2] -
            translate_tuple[0], crop_tuple[3] - translate_tuple[1]))
    return crop_tuples_translated


def clean(inst, clean_level=None):
    """Provides data cleaning based upon clean_level.

    clean_level is set upon Instrument instantiation to
    one of the following:

    'Clean'
    'Dusty'
    'Dirty'
    'None'

    Routine is called by pysat, and not by the end user directly.

    Parameters
    -----------
    inst : (pysat.Instrument)
        Instrument class object, whose attribute clean_level is used to return
        the desired level of data selectivity.

    Returns
    --------
    Void : (NoneType)
        data in inst is modified in-place.

    Note
    ----
        Supports 'clean', 'dusty', 'dirty', 'none'

    """
    if clean_level != 'none':
        print("Cleaning actions for ICON MIGHTI aren't yet defined.")
    return


@classmethod
def query_active_route_ids(cls, session):
    """
        return an array of route_id / agency_id pairs
        {route_id:'2112', agency_id:'C-TRAN'}
        """
    ret_val = []
    routes = cls.query_active_routes(session)
    for r in routes:
        ret_val.append({'route_id': r.route_id, 'agency_id': r.agency_id})
    return ret_val


@classmethod
def update_options(cls, options, items):
    """
        Allows updating options depending on class attributes
        and unvalidated options.
        """
    pass


def _optional_argument(name, arg):
    """Process an optional argument"""
    if not arg.args:
        arg.args = '--' + name.replace('_', '-'),
    arg.kwargs.update(dest=name)
    return arg


def get_example_frontend_context():
    """Default function for users to override for frontend bio data

    This dictionary populates the frontend template on an individual blog
    page with the blog owner's bio, avatar photo, and any other relevant information.

    Expected to return a dictionary filled with serializable values that will
    be passed to the frontend. By default, no bio info is returned.
    """
    return {'blogSidebarAvatar': '/static/ink_cms/assets/sample.jpg',
        'blogSidebarLabel': 'About Me', 'blogSidebarText':
        '
This snippet is passed to the frontend through the "FRONTEND_CONTEXT" setting within "INK_CONFIG". You can override what appears here within settings.py.

Example photo by Nick Morrison on Unsplash
'
        , 'blogAvatar': '/static/ink_cms/assets/sample.jpg', 'blogSubtitle':
        '', 'blogTitle': 'My Customized Blog Title'}


def hide_axis(plt=None, ax=None, x_or_y=None, remove_gridlines=False,
    remove_labels=False):
    """
    Removes axis of a Matplotlib plot.

    Parameters
    ----------
    plt : matplotlib figure
        The figure to modify. N.B. Either plt or ax can be specified,
        not both.
    ax : matplotlib axis
        The axis to modify. N.B. Either plt or ax can be specified,
        not both.
    x_or_y : str
        The x or y coordinates to modify.
    remove_gridlines : bool, optional, default = False
        Specify whether to fully hide the x or y axis. If set to False
        then just the tick labels are hidden and not the ticks
        themselves.

    References
    ----------
    Stackoverflow : Hiding axis text in matplotlib plots
    """
    if plt is None and ax is None:
        raise ValueError('[gu.hide_axis]: Must specify either plt or ax plots.'
            )
    if plt is not None and ax is not None:
        raise ValueError(
            '[gu.hide_axis]: Can only specify either plt or ax, not both.')
    if x_or_y is None:
        raise ValueError("[gu.hide_axis]: x_or_y only takes either 'x' or 'y'."
            )
    if plt is not None:
        ax = plt.gca()
    if x_or_y == 'x':
        if remove_gridlines:
            ax.axes.get_xaxis().set_ticks([])
        elif not remove_gridlines:
            ax.axes.get_xaxis().set_ticklabels([])
        else:
            raise ValueError(
                'remove_gridlines is a boolean parameter and accepts either True or False'
                )
        if remove_labels:
            ax.set_xlabel('')
    elif x_or_y == 'y':
        if remove_gridlines:
            ax.axes.get_yaxis().set_ticks([])
        elif not remove_gridlines:
            ax.axes.get_yaxis().set_ticklabels([])
        else:
            raise ValueError(
                'remove_gridlines is a boolean parameter and accepts either True or False'
                )
        if remove_labels:
            ax.set_ylabel('')
    else:
        raise ValueError("x_or_y only takes either 'x' or 'y'.")
    return


def CustomLanguageModel(path):
    """
    Custom language model. This is actually a dummy wrapper, and you may also pass the path to your custom .klm model
    directly to the recognizer.

    :param str path: Path to a .klm language model

    :return: path to .klm language model
    :rtype: str
    """
    return path


@staticmethod
def _make_second(sides, x):
    """
        Cyclically rotate sides = (a,b,c) so that x is the second entry"
        """
    i = (sides.index(x) + 2) % len(sides)
    return sides[i:] + sides[:i]


@classmethod
def Register2(cls, *args, **kwargs):
    """
`Register2(...)` is a shortcut for `RegisterAt(2, ...)`

**Also See**

* `phi.builder.Builder.RegisterAt`
* `phi.builder.Builder.RegisterMethod`
        """
    return cls.RegisterAt(2, *args, **kwargs)


def readBits(value, bitmasks):
    """
    Extract bits from the integer value using a list of bit masks.
    bitmasks is a list of tuple (mask, text).

    >>> bitmask = (
    ...    (1, "exec"),
    ...    (2, "write"),
    ...    (4, "read"))
    ...
    >>> readBits(5, bitmask)
    ['exec', 'read']
    >>> readBits(12, bitmask)
    ['read', '8']
    """
    bitset = []
    for mask, item in bitmasks:
        if not value & mask:
            continue
        bitset.append(item)
        value = value & ~mask
    if value:
        bitset.append(str(value))
    return bitset


def get_qname(uri, name):
    """
    Returns an expanded QName from URI and local part. If any argument has boolean value
    `False` or if the name is already an expanded QName, returns the *name* argument.

    :param uri: namespace URI
    :param name: local or qualified name
    :return: string or the name argument
    """
    if not uri or not name or name[0] in ('{', '.', '/', '['):
        return name
    else:
        return '{%s}%s' % (uri, name)


def scheming_organization_schemas(expanded=True):
    """
    Return the dict of organization schemas. Or if scheming_organizations
    plugin is not loaded return None.
    """
    from ckanext.scheming.plugins import SchemingOrganizationsPlugin as p
    if p.instance:
        if expanded:
            return p.instance._expanded_schemas
        return p.instance._schemas


def write_out(out, triple, outfile):
    """
    Take the output from test_triple() and write it to file.
    """
    print(triple[0], '\t', triple[1], '\t', triple[2], '\t', sep='', end='',
        file=outfile)
    print(out['Zscore'], '\t', sep='', end='', file=outfile)
    print(out['Pvalue'], '\t', sep='', end='', file=outfile)
    print(out['Gamma'], '\t', sep='', end='', file=outfile)
    print(out['AAAA'], '\t', sep='', end='', file=outfile)
    print(out['AAAB'], '\t', sep='', end='', file=outfile)
    print(out['AABA'], '\t', sep='', end='', file=outfile)
    print(out['AABB'], '\t', sep='', end='', file=outfile)
    print(out['AABC'], '\t', sep='', end='', file=outfile)
    print(out['ABAA'], '\t', sep='', end='', file=outfile)
    print(out['ABAB'], '\t', sep='', end='', file=outfile)
    print(out['ABAC'], '\t', sep='', end='', file=outfile)
    print(out['ABBA'], '\t', sep='', end='', file=outfile)
    print(out['BAAA'], '\t', sep='', end='', file=outfile)
    print(out['ABBC'], '\t', sep='', end='', file=outfile)
    print(out['CABC'], '\t', sep='', end='', file=outfile)
    print(out['BACA'], '\t', sep='', end='', file=outfile)
    print(out['BCAA'], '\t', sep='', end='', file=outfile)
    print(out['ABCD'], '\n', sep='', end='', file=outfile)


def split_warnings(warnings):
    """Split full warning text into individual warnings."""
    current = []
    for line in warnings.split('\n'):
        line = line.strip()
        if not line:
            continue
        current.append(line)
        if line.endswith(']') and '[-W' in line:
            yield '\n'.join(current)
            current = []
    if current:
        yield '\n'.join(current)


@staticmethod
def read_line(line):
    """Reads lines of XML and delimits, strips, and returns."""
    name, value = '', ''
    if '=' in line:
        name, value = line.split('=', 1)
    return [name.strip(), value.strip()]


def GetChangePoints(cluster_assignment):
    """
    Pass in the result of RunTicc to split into changepoint indexes
    """
    currIndex = -1
    index = -1
    currCluster = -1
    results = []
    for cluster in cluster_assignment:
        if currCluster != cluster:
            if currCluster != -1:
                results.append((currIndex, index, currCluster))
            index += 1
            currIndex = index
            currCluster = cluster
        else:
            index += 1
    results.append((currIndex, index, currCluster))
    return results


def clickselect(extend: bool=False, column: bool=False, curves: bool=False):
    """Select keyframes by clicking on them

    :param extend: Extend Select, Toggle keyframe selection instead of leaving newly selected keyframes only
    :type extend: bool
    :param column: Column Select, Select all keyframes that occur on the same frame as the one under the mouse
    :type column: bool
    :param curves: Only Curves, Select all the keyframes in the curve
    :type curves: bool
    """
    pass


def _check_pyopengl_3D():
    """Helper to ensure users have OpenGL for 3D texture support (for now)"""
    global USE_TEX_3D
    USE_TEX_3D = True
    try:
        import OpenGL.GL as _gl
    except ImportError:
        raise ImportError('PyOpenGL is required for 3D texture support')
    return _gl


def add_call_consensus_arguments(parser):
    """
    Add arguments to a parser for sub-command "call_consensus"
    :param parser: argeparse object
    :return:
    """
    parser.add_argument('-i', '--image_dir', type=str, required=True, help=
        '[REQUIRED] Path to a directory where all MarginPolish generated images are.'
        )
    parser.add_argument('-m', '--model_path', type=str, required=True, help
        =
        '[REQUIRED] Path to a trained model (pkl file). Please see our github page to see options.'
        )
    parser.add_argument('-b', '--batch_size', type=int, required=False,
        default=512, help=
        'Batch size for testing, default is 512. Please set to 512 or 1024 for a balanced execution time.'
        )
    parser.add_argument('-w', '--num_workers', type=int, required=False,
        default=8, help=
        'Number of workers to assign to the dataloader. Shouldg be 0 if using Docker.'
        )
    parser.add_argument('-o', '--output_dir', type=str, required=False,
        default='./output/', help='Path to the output directory.')
    parser.add_argument('-p', '--output_prefix', type=str, required=False,
        default='HELEN_prediction', help=
        'Prefix for the output file. Default is: HELEN_prediction')
    parser.add_argument('-g', '--gpu_mode', default=False, action=
        'store_true', help=
        'If set then PyTorch will use GPUs for inference. CUDA required.')
    parser.add_argument('-d_ids', '--device_ids', type=str, required=False,
        default=None, help=
        """List of gpu device ids to use for inference. Only used in distributed setting.
Example usage: --device_ids 0,1,2 (this will create three callers in id 'cuda:0, cuda:1 and cuda:2'
If none then it will use all available devices."""
        )
    parser.add_argument('-t', '--threads', type=int, required=False,
        default=16, help='Total available threads to use.')
    parser.add_argument('-c', '--callers', type=int, required=False,
        default=8, help=
        'Total number of callers to spawn if doing CPU inference in distributed mode.'
        )
    return parser


def __repr__(A):
    """For `print` and `pprint`"""
    return A.to_str()


def __getitem__(key):
    """
        Get a value by key. If there is no value
        for the key, or it has been evicted, return None.

        This should be considered a hit on the key.

        This never results in raising a `KeyError`.
        """


def isotopes_catalogue(element):
    """
    Catalogue of isotopes for a given element

    Parameters
    ----------
    element : :class:`string`
              Abbreviation of the element name (e.g., "H", "Mn", "Fe", ....)

    Returns
    -------
    field : :class:`list`
            List of strings with all stable isotopes of the element.

    See Also
    --------
    nuclear_properties : Elementary information about a specific isotope

    Notes
    -------

    This is a cataloge of stable isotopes (non-radioactive isotopes) for
    each element reaching from hydrogen to uranium. The function
    nuclear_properties() can be used to get detailed information about a
    specific isotope.

    Examples
    --------

    Get isotopes of nitrogen, calcium and vanadium

    >>> import cwEPRsim.Nucdic as nucdic
    >>> Isotopes = nucdic.isotopes_cataloge("N")
    >>> print(Isotopes)
    ['14N', '15N']
    >>> Isotopes2 = nucdic.isotopes_cataloge("Ca")
    >>> print(Isotopes2)
    ['42Ca', '43Ca', '44Ca', '46Ca', '48Ca']
    >>> Isotopes3 = nucdic.isotopes_cataloge("V")
    >>> print(Isotopes3)
    ['50V', '51V']
    """
    Isotopes = {'H': ['1H', '2H'], 'He': ['3He'], 'Li': ['6Li', '7Li'],
        'Be': ['9Be'], 'B': ['10B', '11B'], 'C': ['12C', '13C'], 'N': [
        '14N', '15N'], 'O': ['16O', '17O', '18O'], 'F': ['19F'], 'Ne': [
        '20Ne', '21Ne', '22Ne'], 'Na': ['22Na', '23Na'], 'Mg': ['24Mg',
        '25Mg', '26Mg'], 'Al': ['27Al'], 'Si': ['29Si', '30Si'], 'P': [
        '31P'], 'S': ['32S', '33S', '34S', '36S'], 'Cl': ['35Cl', '37Cl'],
        'Ar': ['36Ar', '38Ar', '40Ar'], 'K': ['39K', '40K', '41K'], 'Ca': [
        '42Ca', '43Ca', '44Ca', '46Ca', '48Ca'], 'Sc': ['45Sc'], 'Ti': [
        '46Ti', '47Ti', '48Ti', '49Ti', '50Ti'], 'V': ['50V', '51V'], 'Cr':
        ['50Cr', '52Cr', '53Cr', '54Cr'], 'Mn': ['55Mn'], 'Fe': ['54Fe',
        '56Fe', '57Fe', '58Fe'], 'Co': ['59Co'], 'Ni': ['58Ni', '60Ni',
        '61Ni', '62Ni', '64Ni'], 'Cu': ['63Cu', '65Cu'], 'Zn': ['64Zn',
        '66Zn', '67Zn', '68Zn', '70Zn'], 'Ga': ['69Ga', '71Ga'], 'Ge': [
        '70Ge', '72Ge', '73Ge', '74Ge', '76Ge'], 'As': ['75As'], 'Se': [
        '74Se', '76Se', '77Se', '78Se', '80Se', '82Se'], 'Br': ['79Br',
        '81Br'], 'Kr': ['78Kr', '80Kr', '82Kr', '83Kr', '84Kr', '86Kr'],
        'Rb': ['85Rb', '87Rb'], 'Sr': ['84Sr', '86Sr', '87Sr', '88Sr'], 'Y':
        ['89Y'], 'Zr': ['90Zr', '91Zr', '92Zr', '94Zr', '96Zr'], 'Nb': [
        '93Nb'], 'Mo': ['92Mo', '94Mo', '95Mo', '96Mo', '97Mo', '98Mo',
        '100Mo'], 'Tc': ['99Tc'], 'Ru': ['96Ru', '98Ru', '99Ru', '100Ru',
        '101Ru', '102Ru', '104Ru'], 'Rh': ['103Rh'], 'Pd': ['102Pd',
        '104Pd', '105Pd', '106Pd', '108Pd', '110Pd'], 'Ag': ['107Ag',
        '109Ag'], 'Cd': ['106Cd', '108Cd', '110Cd', '111Cd', '112Cd',
        '113Cd', '114Cd', '116Cd'], 'In': ['113In', '115In'], 'Sn': [
        '112Sn', '114Sn', '115Sn', '116Sn', '117Sn', '118Sn', '119Sn',
        '120Sn', '122Sn', '124Sn'], 'Sb': ['121Sb', '123Sb'], 'Te': [
        '120Te', '122Te', '123Te', '124Te', '125Te', '126Te', '128Te',
        '130Te'], 'I': ['127I'], 'Xe': ['124Xe', '126Xe', '128Xe', '129Xe',
        '130Xe', '131Xe', '132Xe', '134Xe', '136Xe'], 'Cs': ['133Cs'], 'Ba':
        ['130Ba', '132Ba', '134Ba', '135Ba', '136Ba', '137Ba', '138Ba'],
        'La': ['138La', '139La'], 'Ce': ['136Ce', '138Ce', '140Ce', '142Ce'
        ], 'Pr': ['141Pr'], 'Nd': ['142Nd', '143Nd', '144Nd', '145Nd',
        '146Nd', '148Nd', '150Nd'], 'Pm': ['147Pm'], 'Sm': ['144Sm',
        '147Sm', '148Sm', '149Sm', '150Sm', '152Sm', '154Sm'], 'Eu': [
        '151Eu', '153Eu'], 'Gd': ['152Gd', '154Gd', '155Gd', '156Gd',
        '157Gd', '158Gd', '160Gd'], 'Tb': ['159Tb'], 'Dy': ['156Dy',
        '158Dy', '160Dy', '161Dy', '162Dy', '163Dy', '164Dy'], 'Ho': [
        '165Ho'], 'Er': ['162Er', '164Er', '166Er', '167Er', '168Er',
        '170Er'], 'Tm': ['169Tm'], 'Yb': ['168Yb', '170Yb', '171Yb',
        '172Yb', '173Yb', '174Yb', '176Yb'], 'Lu': ['175Lu', '176Lu'], 'Hf':
        ['174Hf', '176Hf', '177Hf', '178Hf', '179Hf', '180Hf'], 'Ta': [
        '180Ta', '182Ta'], 'W': ['180W', '182W', '183W', '184W', '186W'],
        'Re': ['185Re', '187Re'], 'Os': ['184Os', '186Os', '187Os', '188Os',
        '189Os', '190Os', '192Os'], 'Ir': ['191Ir', '193Ir'], 'Pt': [
        '190Pt', '192Pt', '194Pt', '195Pt', '196Pt', '198Pt'], 'Au': [
        '197Au'], 'Hg': ['196Hg', '198Hg', '199Hg', '200Hg', '201Hg',
        '202Hg', '204Hg'], 'Tl': ['203Tl', '205Tl'], 'Pb': ['204Pb',
        '206Pb', '207Pb', '208Pb'], 'Bi': ['209Bi'], 'Th': ['232Th'], 'U':
        ['235U', '238U']}
    try:
        return Isotopes[element]
    except KeyError:
        return [element]


@staticmethod
def _set_html_begin():
    """
           start the html page
        """
    return '<html>\n'


def __call__(cls, *args, **kwargs):
    """Called when you call a class type constructor() """
    obj = type.__call__(cls, *args, **kwargs)
    if hasattr(obj, '__post_init__'):
        obj.__post_init__(*args, **kwargs)
    return obj


def duplicate():
    """Make copies of the selected bones within the same armature

    """
    pass


def to_bin(path):
    """Convert to binary if not already

    We want paths to be binary since we can't assume an encoding but
    it shall still be convenient to pass in unicode strings
    """
    return path.encode() if not isinstance(path, bytes) else path


def add_cli_list(subparsers):
    """Create subparser for `list`.
    """
    desc = 'list all instances owned by this user.'
    list_parser = subparsers.add_parser('list', description=desc, help=desc,
        add_help=False)
    list_parser.add_argument('-h', '--help', dest='print_list_help', action
        ='store_true', default=False, help='print this help message and exit')


@staticmethod
def _normalize_mapping(mapping):
    """
    Normalizes the schema mapping.
    """
    key2type = {}
    key2name = {}
    for key, ent in mapping.items():
        if isinstance(ent, (tuple, list)):
            key_type, key_name = ent
        else:
            key_type, key_name = ent, key
        key2type[key] = key_type
        key2name[key] = key_name
    return key2type, key2name


@classmethod
def get_social_auth_for_user(cls, user, provider=None, id=None):
    """Return all the UserSocialAuth instances for given user"""
    raise NotImplementedError('Implement in subclass')


@classmethod
def extract_error_message(cls, e):
    """Extract error message for queries"""
    message = str(e)
    try:
        if isinstance(e.args, tuple) and len(e.args) > 1:
            message = e.args[1]
    except Exception:
        pass
    return message


def encode_uvarint(number: int) ->bytes:
    """Pack `number` into varint bytes."""
    buf = b''
    while True:
        towrite = number & 127
        number >>= 7
        if number:
            buf += bytes((towrite | 128,))
        else:
            buf += bytes((towrite,))
            break
    return buf


def convert_pgts(time):
    """Convert PointGray cameras timestamps to seconds.
    Use convert then uncycle"""
    cycle1 = time >> 12 & 8191
    cycle2 = time >> 25 & 127
    seconds = cycle2 + cycle1 / 8000.0
    return seconds


@classmethod
def from_json(cls, project, data):
    """return instance made from given json
        """
    action = cls(project)
    action._apply_json(data)
    return action


def get_match_string(matches):
    """
    collect and return any matched groups that contain data,
    otherwise, return 'No data available'
    :param matches: an re match object
    """
    group_matches = 0
    match_string = ''
    for name, group in matches.groupdict().items():
        if group is not None:
            group = group.replace('\n', '').strip()
            if group_matches > 0:
                match_string += ', ' + group
            else:
                match_string += group
            group_matches += 1
    if not match_string:
        match_string = 'No data available'
    return match_string


def getText():
    """Returns a list of strings which describe the event. These are
        intended to be displayed in a narrow column. If more space is
        available, the caller should join them together with spaces before
        presenting them to the user."""


def mutations(node):
    """
    Get the mutations on a tree branch. Take compressed sequences from both sides
    of the branch (attached to the node), compute mutations between them, and
    expand these mutations to the positions in the real sequences.
    """
    if node.up is None:
        return []
    elif not node.tt.reconstructed_tip_sequences and node.name in node.tt.data.aln:
        return node.tt.data.differences(node.up.cseq, node.tt.data.aln[node
            .name], seq2_compressed=False)
    elif node.is_terminal() and node.name not in node.tt.data.aln:
        return []
    else:
        return node.tt.data.differences(node.up.cseq, node.cseq)


def is_select(status):
    """Returns true if the first word in status is 'select'."""
    if not status:
        return False
    return status.split(None, 1)[0].lower() == 'select'


@classmethod
def empty(cls):
    """Returns an interval set containing no values.

        >>> print IntervalSet.empty()
        <Empty>
        """
    return cls()


@staticmethod
def check_sum(data):
    """
        checksum = 255 - ((id + length + data1 + data2 + ... + dataN) & 255)
        """
    return 255 - (sum(data) & 255)


def get_account_id_from_arn(trail_arn):
    """Gets the account ID portion of an ARN"""
    return trail_arn.split(':')[4]


def nelnn(param):
    """Return true if 'param' is neither an empty list or none."""
    if not isinstance(param, list) and param is not None:
        return True
    return bool(param)


def build_db_query(fields_names, field_values):
    """ method builds query dictionary by zipping together DB field names with the field values """
    if not isinstance(field_values, (list, tuple)):
        field_values = [field_values]
    if len(fields_names) != len(field_values):
        raise ValueError(
            f'Error: unable to build a primary key query due to mismatch in number of fields {len(fields_names)} vs {len(field_values)}'
            )
    query = dict()
    for k, v in zip(fields_names, field_values):
        query[k] = v
    return query


def fiddle(schema):
    """Fiddle the schema.

        This is a copy of the class' schema, with any ISchemaExtender-provided
        fields added. The schema may be modified in-place: there is no
        need to return a value.

        In general, it will be a bad idea to delete or materially change
        fields, since other components may depend on these ones.

        If you change any fields, then you are responsible for making a copy of
        them first and place the copy in the schema.
        """


def encode_int(buffer: bytearray, position: int, length: int, value: int
    ) ->None:
    """
    Encode the integer value.

    :param buffer: Buffer to fill
    :param position: Start position
    :param length: Chunk length
    :param value: Value to encode
    :return: None
    """
    length -= 1
    position += length
    while length >= 0:
        buffer[position] = value % 10 + ord('0')
        value //= 10
        position -= 1
        length -= 1


def apply_matrix_norm(m, v):
    """Equivalent to apply_matrix_pt(M, (p,q)) - apply_matrix_pt(M, (0,0))"""
    a, b, c, d, e, f = m
    p, q = v
    return a * p + c * q, b * p + d * q


@staticmethod
def handle_session(session, exc=None):
    """
        Handles closing a session.

        :param Session session: The session to close.
        :param Exception exc: The exception raised,
            If an exception was raised, else None
        """
    if exc:
        session.rollback()
    session.close()


def reverse_copy(items):
    """
    Return a list that is a reversed copy of items
    """
    item_copy = items[:]
    item_copy.reverse()
    return item_copy


def p_parameter_list(p):
    """parameter_list : parameter_list parameter"""
    p[0] = p[1]
    p[0].update(p[2])


def nicesize(nbytes: int, space: str='') ->str:
    """
	Uses IEC 1998 units, such as KiB (1024).
	:param nbytes: Number of bytes
	:param space: Separator between digits and units
	:return: Formatted string
	"""
    data = {'PiB': 1024 ** 5, 'TiB': 1024 ** 4, 'GiB': 1024 ** 3, 'MiB':
        1024 ** 2, 'KiB': 1024 ** 1}
    for suffix, scale in data.items():
        if nbytes >= scale:
            break
    else:
        scale, suffix = 1, 'B'
    return str(nbytes // scale) + space + suffix


def date_to_greg(date):
    """
    Convert date in ephem.date format to gregorian date.

    Parameters
    ----------
    date : ephem.Date
        Floating point value used by ephem to represent a date.
        The value is the number of days since 1899 December 31 12:00 UT. When
        creating an instance you can pass in a Python datetime instance,
        timetuple, year-month-day triple, or a plain float.
        Run str() on this object to see the UTC date it represents.
        ...
        WTF?

    Returns
    ----------
    greg : string
        Gregorian date (format: YYYYMMDD_HHMMSS)

    Examples
    ----------
    >>> e = ephem.Observer()
    >>> e.date = 0.0 ## 1899 December 31 12:00 UT
    >>> greg = date_to_greg(e.date)
    >>> print('DATE={} ->'.format(round(e.date, 2)),
    ...     'GREG={}'.format(greg))
    DATE=0.0 -> GREG=18991231_120000
    """
    date_ = str(date)
    date_ = str(date.datetime())
    greg = date_.split('.')[0].replace('-', '').replace(':', '').replace(' ',
        '_')
    return greg


def from_html(color):
    """Convert html hex code to rgb."""
    if len(color) != 7 or color[0] != '#':
        raise ValueError('Invalid length of html code')
    return int(color[1:3], 16), int(color[3:5], 16), int(color[5:7], 16)


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

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


def render(data_dict, *args, **kwargs):
    """
        Render the data_dict into output in some fashion.  This function must
        access both ``*args`` and ``**kwargs`` to allow an application to mix
        output handlers that support different features.

        :param data_dict: The dictionary whose data we need to render into
            output.
        :returns: string or unicode string or None

        """


def bits(bytes=None, kilobytes=None, megabytes=None, gigabytes=None,
    terabytes=None, petabytes=None):
    """Usage: Convert to bits. Example: bits(megabytes=23)"""
    if bytes is not None:
        return bytes * 8
    elif kilobytes is not None:
        return kilobytes * 8000
    elif megabytes is not None:
        return megabytes * 8000000.0
    elif gigabytes is not None:
        return gigabytes * 8000000000.0
    elif terabytes is not None:
        return terabytes * 8000000000000.0
    elif petabytes is not None:
        return petabytes * 8000000000000000.0
    else:
        raise Exception(
            'You must specify one value. Example: bytes, kilobytes, megabytes, gigabytes, terabytes, petabytes'
            )


def _get_list_of_str_from_csv(csv):
    """
    Get a list of strings from one line CSV.

    Parameters
    ----------
    csv : str
        One line CSV string.

    Returns
    -------
    result_list : list of str
        List of splitted string.
    """
    if csv == '':
        return []
    return csv.split(',')


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


def __before_delete(collection, _sa_initiator=None):
    """Special method to run 'commit existing value' methods"""
    executor = collection._sa_adapter
    if executor:
        executor.fire_pre_remove_event(_sa_initiator)


def decrypt(enc, key, hmac_key=None, key_is_hash=False, b64=True, bits=256):
    """
    Decrypt encoded data with AES-CBC

    Args:
        enc: data to decrypt
        key: decryption key
        key_is_hash: consider decryption key is sha256 hash
        hmac_key: HMAC key (optional), True or custom key
        b64: decode data from base64 (default: True)
        bits: key size (128, 192 or 256, default is 256)
    Raises:
        ValueError: if HMAC auth failed

    note: if hmac_key is True and key is hash, sha512 sum is required
    """
    from Crypto.Cipher import AES
    import hashlib
    import hmac
    if b64:
        import base64
        enc = base64.b64decode(enc)
    if hmac_key is True:
        h = key if key_is_hash else hashlib.sha512(key.encode() if
            isinstance(key, str) else key).digest()
        keyhash = h[:bits // 8]
        hmac_key = h[:-32]
    else:
        keyhash = key if key_is_hash else hashlib.sha256(key.encode() if
            isinstance(key, str) else key).digest()[:bits // 8]
    iv = enc[:16]
    if hmac_key:
        if hmac.new(hmac_key.encode() if isinstance(hmac_key, str) else
            hmac_key, enc[:-32], hashlib.sha256).digest() != enc[-32:]:
            raise ValueError('HMAC auth failed')
    cipher = AES.new(keyhash, AES.MODE_CBC, iv)
    data = cipher.decrypt(enc[16:-32 if hmac_key else None])
    return data[:-data[-1]]


def rescale_boxes(boxes, current_dim, original_shape):
    """ Rescales bounding boxes to the original shape """
    orig_h, orig_w = original_shape
    pad_x = max(orig_h - orig_w, 0) * (current_dim / max(original_shape))
    pad_y = max(orig_w - orig_h, 0) * (current_dim / max(original_shape))
    unpad_h = current_dim - pad_y
    unpad_w = current_dim - pad_x
    boxes[:, (0)] = (boxes[:, (0)] - pad_x // 2) / unpad_w * orig_w
    boxes[:, (1)] = (boxes[:, (1)] - pad_y // 2) / unpad_h * orig_h
    boxes[:, (2)] = (boxes[:, (2)] - pad_x // 2) / unpad_w * orig_w
    boxes[:, (3)] = (boxes[:, (3)] - pad_y // 2) / unpad_h * orig_h
    return boxes


@classmethod
def take_prefixed(cls, iterable, prefix):
    """
        Side effect: pop :arg`mapping` keys, starting with :arg`prefix`
        """
    attrs = {}
    for key in iterable.copy().keys():
        if key.startswith(prefix):
            attrs[key[len(prefix):]] = iterable.pop(key)
    return cls(attrs)


def filterCommands(pass_off_commands, commands):
    """
    Puts items of pass_off_commands in commands if commands does not already have them
    Args:
        pass_off_commands (list|tuple): Commands manually fed to be passed off
        commands (list): The automatically generate commands to be passed off
    Returns (list): The processed commands
    """
    if pass_off_commands:
        for item in pass_off_commands:
            if item[0] == '-':
                if item not in commands:
                    commands.append(item)


@staticmethod
def has_battery():
    """Return whether the device has a battery."""
    return False


@classmethod
def fromdict(cls, mapping, with_dto=False):
    """Create a new Data Transfer Object (DTO) from a regular
        Python dictionary.
        """
    return cls(mapping).as_dto(with_dto=with_dto)


def singletagmerge(p1nodes, p2nodes):
    """
    merge the nodes corresponding to a single tag

    Note that the inputs are lists of node-linenum pairs (i.e. not just lists
    of nodes)
    """
    if not p2nodes:
        return p1nodes
    if not p1nodes:
        return p2nodes
    p1currentnode = p1nodes[-1][0]
    p2currentnode = p2nodes[-1][0]
    if p1currentnode != p2currentnode and len(p1nodes) == len(p2nodes):
        return None
    if len(p1nodes) >= len(p2nodes):
        hrnodes, lrnodes = p1nodes, p2nodes
    else:
        hrnodes, lrnodes = p2nodes, p1nodes
    commonidx = len(lrnodes)
    for n in range(len(lrnodes)):
        if hrnodes[n][0] != lrnodes[n][0]:
            commonidx = n
            break
        lrnodes[n][1] = p1nodes[n][1]
    return lrnodes + hrnodes[commonidx:]


def _add_punctuation(text):
    """Add punctuation at the end of each chunk.

    Mimic2 expects some form of punctuation at the end of a sentence.
    """
    punctuation = ['.', '?', '!', ';']
    if len(text) >= 1 and text[-1] not in punctuation:
        return text + '.'
    else:
        return text


def newest_folder(path):
    """Return newest folder on path"""
    list_of_paths = path.glob('*')
    return max(list_of_paths, key=lambda p: p.stat().st_ctime)


@staticmethod
def _pg_cast(enum):
    """
        Returns a function to cast a SQL enum to the enumeration type
        corresponding to `enum`. Namely, `enum` should be a member of
        `nfldb.Enums`.
        """
    return lambda sqlv, _: None if not sqlv else enum[sqlv]


def segment_basic_score(y_true_seg, y_pred_seg):
    """
    Compares y_true_seg with y_pred_seg and returns the corresponding label

    :param y_true_seg: true value of segment
    :param y_pred_seg: predicted value of segment
    :return: label that indicates True Positive, True Negative, False Positive or False Negative.
    Possible outcomes: "TP", "TN", "FP", or "FN".
    """
    true_vs_pred = {(True, True): 'TP', (True, False): 'FN', (False, True):
        'FP', (False, False): 'TN'}
    return true_vs_pred[y_true_seg, y_pred_seg]


def list_numbering_start(attrs):
    """extract numbering from list element attributes"""
    if 'start' in attrs:
        return int(attrs['start']) - 1
    else:
        return 0


@staticmethod
def get_filelines(filename) ->[str]:
    """Get the lines in a file."""
    with open(filename, 'rb') as f:
        filetext = f.read()
    return str(filetext, encoding='utf-8').split('\n')


@classmethod
def of(cls, obj):
    """
        Get the usage expectation of a given object.

        :param obj:
            The object for which usage expectation is to be set
        :returns:
            Either a previously made object or a fresh instance of
            :class:`UsageExpectation`.
        """
    try:
        return obj.__usage_expectation
    except AttributeError:
        ua = cls(type(obj))
        obj.__usage_expectation = ua
        return ua


def geom_size(geom, dim=None):
    """Returns the size of a volume or sinogram, based on the projection or volume geometry.

    :param geom: Geometry to calculate size from
    :type geometry: :class:`dict`
    :param dim: Optional axis index to return
    :type dim: :class:`int`
    """
    if 'GridSliceCount' in geom:
        s = geom['GridSliceCount'], geom['GridRowCount'], geom['GridColCount']
    elif 'GridColCount' in geom:
        s = geom['GridRowCount'], geom['GridColCount']
    elif geom['type'] == 'parallel' or geom['type'] == 'fanflat':
        s = len(geom['ProjectionAngles']), geom['DetectorCount']
    elif geom['type'] == 'parallel3d' or geom['type'] == 'cone':
        s = geom['DetectorRowCount'], len(geom['ProjectionAngles']), geom[
            'DetectorColCount']
    elif geom['type'] == 'fanflat_vec':
        s = geom['Vectors'].shape[0], geom['DetectorCount']
    elif geom['type'] == 'parallel3d_vec' or geom['type'] == 'cone_vec':
        s = geom['DetectorRowCount'], geom['Vectors'].shape[0], geom[
            'DetectorColCount']
    if dim != None:
        s = s[dim]
    return s


def measure_strehl(HDUlist_or_filename=None, ext=0, slice=0, center=None,
    display=True, verbose=True, cache_perfect=False):
    """ Measure Strehl for a PSF

    NOTE - deprecated / removed function.  Moved to webbpsf package instead.

    This stub is just here to provide information on that transfer,
    and will be removed in a future version of poppy.
    """
    import warnings
    warnings.warn(
        'measure_strehl function has been deprecated and moved to webbpsf instead of poppy.'
        , DeprecationWarning)
    raise NotImplementedError(
        "The 'measure_strehl' function has been moved from the 'poppy' package to the 'webbpsf' package. "
         + 'See https://github.com/mperrin/poppy/issues/138')


def get_dataset_name_from_table_id(table_id):
    """ Extracts dataset name from table_id

    :param table_id: str
    :return: str
    """
    return table_id.split('__')[1]


def nl_init_list_head(head):
    """https://github.com/thom311/libnl/blob/libnl3_2_25/include/netlink/list.h#L90"""
    head.next_ = head
    head.prev = head


def _verify_np_symbol(op_name, func_name, sym):
    """Verify if the sym is a numpy symbol.

    Parameters
    ----------
    op_name : str
        Operator full name registered in backend.
    func_name : str
        Operator name exposed to users. This is usually the name by stripping off
        the prefix of the full operator names registered in backend.
    sym : symbol to be verified
    """
    from .numpy._symbol import _Symbol as np_symbol
    if not isinstance(sym, np_symbol):
        raise TypeError(
            'Operator `{}` registered in backend is known as `{}` in Python. This is a numpy operator which can only accept MXNet numpy ndarrays, while received a legacy ndarray. Please ensure that you have activated numpy semantics by calling `npx.set_np()` in your code. If you still see this error with numpy semantics activated, please call `as_np_ndarray()` upon the legacy ndarray to convert it to an MXNet numpy ndarray, and then feed the converted array to this operator.'
            .format(op_name, func_name))


def select_border(gesture_mode: int=0, xmin: int=0, xmax: int=0, ymin: int=
    0, ymax: int=0, extend: bool=True):
    """Select all time markers using border selection

    :param gesture_mode: Gesture Mode
    :type gesture_mode: int
    :param xmin: X Min
    :type xmin: int
    :param xmax: X Max
    :type xmax: int
    :param ymin: Y Min
    :type ymin: int
    :param ymax: Y Max
    :type ymax: int
    :param extend: Extend, Extend selection instead of deselecting everything first
    :type extend: bool
    """
    pass


def listify(obj):
    """ Wraps all non-list or tuple objects in a list; provides a simple
    way to accept flexible arguments. """
    if obj is None:
        return []
    else:
        return obj if isinstance(obj, (list, tuple, type(None))) else [obj]


@classmethod
def __prepare__(mcs, name, bases, **kwargs):
    """Metaclass magic for object storage."""
    return {}


def create_tasks(folder, task_queue, scandir=scandir, get_path=get_path):
    """
        Creates one task for each file on a folder
    """
    tasks = []
    for idx, f in enumerate(scandir(folder)):
        path = get_path(folder, f)
        task = idx, path
        tasks.append(task)
        task_queue.put(task)
    return tasks


def get_functions(schema_name):
    """
    Return a list of triggers within a schema with given name.

    NOT IMPLEMENTED; TODO:
    """
    q = """
    SELECT  proname, prosrc
    FROM    pg_catalog.pg_namespace n
    JOIN    pg_catalog.pg_proc p
    ON      pronamespace = n.oid
    WHERE   nspname = 'public';
    """


@classmethod
def deserialize(cls, graphson_type, value):
    """
        Deserialize a `graphson_type` value to a python object.

        :param graphson_base_type: The graphson graphson_type. e.g. 'gx:Instant'
        :param value: The graphson value to deserialize.
        """
    return cls.get_deserializer(graphson_type).deserialize(value)


def allowGroupAdd(principal_id, group_id):
    """True iff this plugin will allow adding a certain principal to a
        certain group."""


def obfuscate(text):
    """obfuscate text so it is recognizable without divulging it

    >>> obfuscate('12345')
    '1***5'

    >>> obfuscate('')
    ''

    """
    return text[:1] + '*' * (len(text) - 2) + text[-1:]


def margin_of_victory(value_list):
    """
    Accepts a list of numbers and returns the difference between
    the first place and second place values.

    This can be useful for covering elections as an easy to way to figure out
    the margin of victory for a leading candidate.

    h3. Example usage

        >> import calculate
        >> calculate.margin_of_victory([3285, 2804, 7170])
        3885
    """
    value_list.sort(reverse=True)
    return value_list[0] - value_list[1]


def create_dataset_showcase(dataset, showcase, **kwargs):
    """
    Create dataset and showcase

    Args:
        dataset (Dataset): Dataset to create
        showcase (Showcase): Showcase to create
        **kwargs: Args to pass to dataset create_in_hdx call

    Returns:
        None

    """
    dataset.update_from_yaml()
    dataset.create_in_hdx(remove_additional_resources=True, hxl_update=
        False, **kwargs)
    showcase.create_in_hdx()
    showcase.add_dataset(dataset)


def WordSeries(words, conjunction='and'):
    """Convert a list of words to an English-language word series.

  Args:
    words: A list of word strings.
    conjunction: A coordinating conjunction.

  Returns:
    A single string containing all the words in the list separated by commas,
    the coordinating conjunction, and a serial comma, as appropriate.
  """
    num_words = len(words)
    if num_words == 0:
        return ''
    elif num_words == 1:
        return words[0]
    elif num_words == 2:
        return (' %s ' % conjunction).join(words)
    else:
        return '%s, %s %s' % (', '.join(words[:-1]), conjunction, words[-1])


@staticmethod
def _get_contract_code():
    """
        Returns the APIC class name for this type of contract.

        :returns: String containing APIC class name for this type of contract.
        """
    return 'vzBrCP'


def _(x):
    """Identity function for string extraction."""
    return x


@classmethod
def _from_dict(cls, _dict):
    """Initialize a AnalyticsEngineCustomAction object from a json dictionary."""
    return cls.from_dict(_dict)


def calculate_zf_NAP(df, z_id):
    """
    Assign the zf with respect to NAP to each layer of a DataFrame.

    :param df: Original DataFrame.
    :param z_id: (float) Elevation with respect to the NAP of my field.
    :return: DataFrame with zf_NAP column.
    """
    return df.assign(zf_NAP=z_id - df['zf'])


@staticmethod
def construct_bap_id(subscription_id, resource_group_name, lb_name,
    address_pool_name):
    """construct bap id"""
    return (
        '/subscriptions/{}/resourceGroups/{}/providers/Microsoft.Network/loadBalancers/{}/backendAddressPools/{}'
        .format(subscription_id, resource_group_name, lb_name,
        address_pool_name))


def p_fun_def(p):
    """fun_def : FUN ID LPAREN id_list RPAREN LBRACE statement_list RBRACE"""
    p[0] = 'fun_def', p[2], p[4], p[7]


@classmethod
def classLog(cls, setLogging=None):
    """get/set global logging"""
    if setLogging is None:
        return cls.__log
    elif isinstance(setLogging, bool):
        cls.__log = setLogging


def get_time_stamp() ->int:
    """Returns the system time stamp.

    :return:  The system time stamp.
    """
    pass


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


def product(sequence):
    """Return the product of all elements in a sequence."""
    p = 1
    for f in sequence:
        p *= f
    return p


@classmethod
def run(cls, h5_path, base_layer, excl_dict, power_density=1, hsds=False,
    min_area=None, kernel='queen', check_layers=False):
    """
        compute tech-potential

        Parameters
        ----------
        h5_path : str
            Path to .h5 file containing CF means and exclusion layers
        base_layer : str
            Name of dataset in .h5 file containing base layer
        excl_dict : dcit
            Dictionary of exclusion LayerMask arugments {layer: {kwarg: value}}
        power_density : float
            Multiplier to convert CF means to generation means
        hsds : bool
            Boolean flag to use h5pyd to handle .h5 'files' hosted on AWS
            behind HSDS
        min_area : float | NoneType
            Minimum required contiguous area in sq-km
        kernel : str
            Contiguous filter method to use on final exclusion
        check_layers : bool
            Run a pre-flight check on each layer to ensure they contain
            un-excluded values

        Returns
        -------
        mask : ndarray
            Base layer with exclusions masked out
        """
    with cls(h5_path, base_layer, excl_dict, power_density=power_density,
        hsds=hsds, min_area=min_area, kernel=kernel, check_layers=check_layers
        ) as f:
        mask = f.mask
    return mask


def get_dependencies(cells):
    """
    Retrieves the 'install.packages(..)' commands from the cells.

    :param cells: the cells to iterate
    :type cells: list
    :return: the list of dependencies (instances of "pip install ...")
    :rtype: list
    """
    result = []
    for cell in cells:
        if 'cell_type' in cell and cell['cell_type'
            ] == 'code' and 'source' in cell:
            for line in cell['source']:
                if 'install.packages(' in line:
                    result.append(line)
    return result


def _word_maker(arr):
    """
    Helper function Make a string out of pdb stats header "name" variables
    """
    make_a_word = []
    for this in arr:
        if this:
            make_a_word.append(this)
        else:
            break
    return ''.join(make_a_word)


def tokenFromRequest(request):
    """Extract token from header of query parameter.

    :param request: The request to look for an access token in.
    :type request: twisted.web.server.Request

    :return: The token or None if not found
    :rtype: unicode or None
    """
    token = None
    authHeader = request.getHeader('Authorization')
    if authHeader is not None and authHeader.startswith('Bearer '):
        token = authHeader[len('Bearer '):]
    if token is None and 'access_token' in request.args:
        token = request.args['access_token'][0]
    if token and isinstance(token, bytes):
        token = token.decode('UTF-8')
    return token


def insert(index, header, pattern, chain=None, tag=None):
    """Insert a header match rule.

        Inserts the given rule at the given index position in this
        mailing list's header match list.

        :param index: The index to insert the rule at.
        :type index: integer
        :param header: The email header to filter on.  It will be converted to
            lower case for consistency.
        :type header: string
        :param pattern: The regular expression to use.
        :type pattern: string
        :param chain: The chain to jump to, or None to use the site-wide
            antispam jump chain via the configuration.  Defaults to None.
        :type chain: string or None
        :param tag: An arbitrary value to identify a set of IHeaderMatches.
        :type tag: string or None
        :raises ValueError: if the header/pattern pair already exists for this
            mailing list.
        """


@staticmethod
def query(dbversion=None):
    """Returns query to fetch Rule instances from the catalogs"""
    return """
            SELECT nspname AS schema, relname AS table, rulename AS name,
                   split_part('select,update,insert,delete', ',',
                       ev_type::int - 48) AS event, is_instead AS instead,
                   pg_get_ruledef(r.oid) AS definition,
                   obj_description(r.oid, 'pg_rewrite') AS description, r.oid
            FROM pg_rewrite r JOIN pg_class c ON (ev_class = c.oid)
                 JOIN pg_namespace n ON (relnamespace = n.oid)
            WHERE relkind = 'r'
              AND (nspname != 'pg_catalog' AND nspname != 'information_schema')
            ORDER BY nspname, relname, rulename"""


@classmethod
def get_preview_sessionkey(cls):
    """
        Get the session key used for preview. You should not
        need to override this.
        """
    sessionkey = 'cradmin_legacy__{module}.{classname}'.format(module=cls.
        __module__, classname=cls.__name__)
    return sessionkey


def hide(unselected: bool=False):
    """Hide selected particles

    :param unselected: Unselected, Hide unselected rather than selected
    :type unselected: bool
    """
    pass


@classmethod
def get_unblocked_sellables(cls, store, storable=False, supplier=None,
    consigned=False):
    """
        Returns unblocked sellable objects, which means the
        available sellables plus the sold ones.

        :param store: a store
        :param storable: if `True`, only return sellables that also are
          |storable|
        :param supplier: a |supplier| or ``None``, if set limit the returned
          object to this |supplier|

        :rtype: queryset of sellables
        """
    query = cls.get_unblocked_sellables_query(store, storable, supplier,
        consigned)
    return store.find(cls, query)


def stopThreads(wait=False):
    """Attempts to stop all threads.  Threads stop immediately after
        the current item is being processed.

        Returns True if successful, or False if threads are still running
        after waiting 5 seconds for each one.
        """


def pyramid_tm(config):
    """Add beaker includeme in pyramid configuration

    :param config: Pyramid configurator instance
    """
    config.include('pyramid_tm')


def fmodifier_paste(only_active: bool=True, replace: bool=False):
    """Add copied F-Modifiers to the selected NLA-Strips

    :param only_active: Only Active, Only paste F-Modifiers on active strip
    :type only_active: bool
    :param replace: Replace Existing, Replace existing F-Modifiers, instead of just appending to the end of the existing list
    :type replace: bool
    """
    pass


def avg_angle(theta):
    """A function that finds the avage of an array of angles that are
    in the range [-180, 180].

    Parameters
    ----------
    theta : array
        An array of angles that are in the range [-180, 180] degrees

    Returns
    -------
    average : float
        The average angle
    """
    if theta.max() - theta.min() > 180:
        theta[theta < 0] += 360
    return theta.mean()


def wrap(text, width, prefix=''):
    """
    Simple word-wrapping thing. Might be worth considering a
    dependency on 'textwrap' if this gets longer than a few lines ;)
    """
    words = text.split()
    lines = []
    while len(words):
        line = prefix
        while len(words) and len(line) < width:
            word = words.pop(0)
            line += ' ' + word
        lines.append(line)
    return '\n'.join(lines)


def escape(string, size=55):
    """Escape string for dot file"""
    if not size or not string:
        return ''
    if len(string) > size:
        half_size = (size - 5) // 2
        string = string[:half_size] + ' ... ' + string[-half_size:]
    return '' if string is None else string.replace('"', '\\"')


def print_report(outliers, criterion, cutoff, manual_cutoffs):
    """Report per-taxon cutoff value and no of removed alignment positions.

    Construct report string given outliers dict
    {taxon : (taxon_mean_distance, outlier_sequence_ranges)}."""
    report_string = ''
    for taxon, tpl in sorted(outliers.items()):
        cutoff_value, outliers_list = tpl
        if manual_cutoffs:
            for group in manual_cutoffs:
                manual_taxon_name, manual_cutoff_value = group
                if taxon == manual_taxon_name:
                    cutoff_value = manual_cutoff_value
        ranges = ''
        total_seq_removed_from_taxon = 0
        for outlier_range in outliers_list:
            start, end = outlier_range
            seq_removed = int(end) - int(start)
            one_range = '{}-{}\t'.format(start, end)
            ranges += one_range
            total_seq_removed_from_taxon += seq_removed
        report_string += ('{}:\nCutoff: {}\nRemoved {} positions\n{}\n\n'.
            format(taxon, cutoff_value, total_seq_removed_from_taxon, ranges))
    return report_string


def draw_genome(n=None, permutation=None, orientation_list=None,
    save_as_pgf=False, show=True):
    """Produce a drawing of a circular genome with n regions"""
    import matplotlib
    import matplotlib.pyplot as plt
    if save_as_pgf:
        matplotlib.use('pgf')
        matplotlib.rcParams.update({'pgf.texsystem': 'pdflatex',
            'font.family': 'serif', 'text.usetex': True, 'pgf.rcfonts': False})
    import numpy as np
    region_labels = None
    orientation_list = None
    if permutation:
        n = len(permutation)
        region_labels = [None for _ in range(1, n + 1)]
        orientation_list = [(False) for _ in range(1, n + 1)]
        for i, r in enumerate(permutation):
            region_labels[abs(r) - 1] = i + 1
            orientation_list[abs(r) - 1] = r < 0
        region_labels = list(reversed(region_labels))
        orientation_list = list(reversed(orientation_list))
    else:
        pass
    segment_sizes = [(100 / n) for _ in range(n)]
    segment_colors = ['white' for _ in range(n)]
    fig1, ax1 = plt.subplots(figsize=(2, 2))
    wedges, texts = ax1.pie(segment_sizes, labeldistance=1.2, colors=
        segment_colors, labels=region_labels, radius=1, startangle=90,
        wedgeprops={'edgecolor': '0', 'linewidth': 1, 'linestyle': 'solid',
        'antialiased': True})
    if orientation_list:
        for i, p in enumerate(wedges):
            ang = (p.theta2 - p.theta1) / 2.0 + p.theta1
            y = np.sin(np.deg2rad(ang))
            x = np.cos(np.deg2rad(ang))
            arrow_length = 1e-05
            angle = np.deg2rad(ang) + (np.pi / 2 if orientation_list[i] else
                3 * np.pi / 2)
            if orientation_list:
                ax1.arrow(x, y, arrow_length * np.cos(angle), arrow_length *
                    np.sin(angle), overhang=0.3, head_width=0.1,
                    head_length=0.1, head_starts_at_zero=True,
                    length_includes_head=True, linewidth=1, facecolor=
                    'black', fill=True)
    centre_circle = plt.Circle((0, 0), 0.85, fc='white')
    fig = plt.gcf()
    fig.gca().add_artist(centre_circle)
    ax1.axis('equal')
    plt.tight_layout()
    import os
    if not os.path.exists('output'):
        os.makedirs('output')
    plt.savefig('output/genome.png', facecolor='white', transparent=True)
    plt.savefig('output/genome' + ('.pgf' if save_as_pgf else '.pdf'),
        facecolor='white', transparent=True)
    if show and not save_as_pgf:
        plt.show()


def t_KW(t):
    """UAG|HAG|ASG|RULE|CALC"""
    t.type = t.value
    return t


def is_empty(G):
    """Returns True if `G` has no edges.

    Parameters
    ----------
    G : graph
        A NetworkX graph.

    Returns
    -------
    bool
        True if `G` has no edges, and False otherwise.

    Notes
    -----
    An empty graph can have nodes but not edges. The empty graph with zero
    nodes is known as the null graph. This is an $O(n)$ operation where n
    is the number of nodes in the graph.

    """
    return not any(G.adj.values())


def probit(x):
    """
    Applies the CDF from the Normal distribution as the activation rather than a sigmoid
    """
    from tensorflow_probability import distributions
    return distributions.Normal(0, 1).cdf(x)


@staticmethod
def _get_commit_message():
    """ Return the commit message from the user
        """
    return input('Please enter a commit message')


def time_str_to_minutes(time_str):
    """
    通过时间字符串计算得到这是一天中第多少分钟
    :param time_str: eg: '11:10:00'
    :return: int
    """
    time_arr = time_str.split(':')
    hours = int(time_arr[0])
    minutes = int(time_arr[1])
    return hours * 60 + minutes


def list_rep(dumper, data):
    """
    Ensure pyyaml treats our list as a regular list
    """
    return dumper.represent_list(list(data))


def _check_checksum(msg):
    """Ensure checksum in message is good."""
    checksum = int(msg[-2:], 16)
    for char in msg[:-2]:
        checksum += ord(char)
    if checksum % 256 != 0:
        raise ValueError('Elk message checksum invalid')


def get_worker_type(task):
    """Given a task dict, return the workerType.

    Args:
        task (dict): the task dict.

    Returns:
        str: the workerType.

    """
    return task['workerType']


def discard(uid):
    """Remove uid from set.  Ignore if not a member"""


@staticmethod
def encode(obj):
    """
        Defines the underlying format used during traces or services encoding.
        This method must be implemented and should only be used by the internal functions.
        """
    raise NotImplementedError


def search(problem, pop, push):
    """
    a generic solution for searching for a goal state in a problem given the frontier strategy implemented in pop and push functions
    :param problem: the problem
    :param pop: a function to get and remove next state from frontier
    :param push: a function to push a state to the frontier
    :return: list of actions to get to a goal state
    """
    state = problem.getStartState()
    if problem.isGoalState(state):
        return []
    actions = []
    problem.explore(state)
    while True:
        successors = problem.getSuccessors(actions, state)
        for a, s in successors:
            actionsHistory = actions + [a]
            push(actionsHistory, s)
        while problem.isExplored(state):
            next = pop()
            if not next:
                return None
            actions, state = next
        if problem.isGoalState(state):
            return actions
        problem.explore(state)
    return None


def create_geodict_structure(gd=None):
    """
    Creates the geojson dictionnary structure.

    :param gd: an empty OrderedDict
    :return: None
    """
    if gd is not None:
        gd['type'] = 'FeatureCollection'
        gd['crs'] = {'type': 'name', 'properties': {'name':
            'urn:ogc:def:crs:OSG:2:84'}}
        gd['features'] = []


@staticmethod
def _parse_line(line):
    """Parse a line from an SFM file into a tag and a value."""
    line = line.strip()
    if not line:
        return None, None
    if line[0] == '#':
        return None, None
    parsed = iter(line.split(maxsplit=1))
    tag = next(parsed, None)
    value = next(parsed, None)
    return tag, value


def calculate_confidence(inspected, expected, percentage):
    """
    Generate a confidence value possibly confirming or denying data parity.

    A positive return value indicates the amount of bytes past the confidence
    interval. A negative value indicates the missing amount required to pass.

    Args:
        inspected (number): value to calculate the percentage of compared to the expected value.
        expected (number): value that should be confirmed against.
        percentage (number): percentage amount that can be ignored and is still confident.

    Returns:
        Calculated confidence total value.
    """
    return -(expected - (inspected + expected * percentage))


def addon_remove(module: str=''):
    """Delete the add-on from the file system

    :param module: Module, Module name of the add-on to remove
    :type module: str
    """
    pass


def escape(part):
    """
    Escape reserved characters in the name part.
    """
    part = part.replace(',', '\\,')
    return part


def extract_value(dictionary, *args):
    """
    This method extracts a value from a dictionary or a list with nested key values
    :param dict dictionary: A dictionary to extract value from
    :param args: list of keys
    """
    val = dictionary
    try:
        for arg in args:
            val = val[arg]
        return val
    except KeyError:
        return ''
    except IndexError:
        return ''


def debug_info():
    """Return debugging data about objects in the cache.

        o Return a sequence of tuples, (oid, refcount, typename, state).
        """


@staticmethod
def teardown():
    """
        Runs class-wide teardown logic (e.g. close a training session shared across trials).
        """
    pass


def check_event_time(parser, time):
    """
    Checks that the specified time for an event makes sense
    """
    if time < 0:
        parser.error('Event times must be non-negative.')