import itertools
import logging
import math
from typing import Callable, Iterator, Optional, Sequence, Tuple, cast

import numpy as np  # type: ignore

def dp_standard_deviation(
    sample: Sequence[float],
    mean: float,
    epsilon: float,
    beta: float,
    m: int,
    L: float,
    U: float,
    seed: Optional[int] = None,
) -> float:
    """
    Parameters
    ----------
    sample : Sequence[float]
        The sample to compute the standard deviation for, in a
        differentially private manner.
    mean : float
    epsilon : float
        The differential privacy parameter.
    beta : float
        The beta-smooth sensitivity parameter.
    m : int, optional
        The trimming parameter (discard the m smallest and m largest
        elements in `sample`.
    seed : int
        A random seed used to draw from the Cauchy distribution.
    """
    return _dp_disp("std", sample, mean, epsilon, beta, m, L, U, seed)

def dp_variance(
    sample: Sequence[float],
    mean: float,
    epsilon: float,
    beta: float,
    m: int,
    L: float,
    U: float,
    seed: Optional[int] = None,
) -> float:
    """
    Parameters
    ----------
    sample : Sequence[float]
        The sample to compute the variance for, in a differentially
        private manner.
    mean : float
    epsilon : float
        The differential privacy parameter.
    beta : float
        The beta-smooth sensitivity parameter.
    m : int, optional
        The trimming parameter (discard the m smallest and m largest
        elements in `sample`.
    seed : int
        A random seed used to draw from the Cauchy distribution.
    """
    return _dp_disp("var", sample, mean, epsilon, beta, m, L, U, seed)

def _dp_disp(
    dispersion: str,
    sample: Sequence[float],
    mean: float,
    epsilon: float,
    beta: float,
    m: int,
    L: float,
    U: float,
    seed: Optional[int] = None,
) -> float:
    if dispersion == "std":
        dispersion_fun = np.std
    elif dispersion == "var":
        dispersion_fun = np.var
    else:
        raise ValueError("Expected one of 'var' or 'std'. Got {}.".format(dispersion))
    assert epsilon > 0 and beta > 0 and m >= 0 and L < U
    logging.debug("calculating dp variance for %s", str(sample))
    _sample = np.array(sample)
    _sample.sort()
    trimmed_sample = _sample[m : len(sample) - m]
    s = smooth_sensitivity(trimmed_sample, mean, beta, L, U, dispersion)
    scale = math.sqrt(2) * s / epsilon
    rng = np.random.default_rng(seed=seed)
    noise = rng.standard_cauchy() * scale
    return dispersion_fun(trimmed_sample) + noise  # type: ignore

def smooth_sensitivity(
    sample: np.ndarray, mean: float, beta: float, L: float, U: float, dispersion: str
) -> float:
    """Compute the beta-smooth sensitivity of a dispersion function on
    the given sample.

    Parameters
    ----------
    sample : Sequence[float]
        The sample to compute the beta-smooth sensitivity of the std
        function on. It is assumed to be sorted.
    mean : float
    beta : float
        The decaying factor in the smooth sensitivity term.
    L : float
        The domain specific lower bound on the values of `sample`.
    U : float
        The domain specific upper bound on the values of `sample`.
    dispersion : str
        One of 'var' or 'std'.
    """
    if dispersion == "std":
        dispersion_fun = np.std
    elif dispersion == "var":
        dispersion_fun = np.var
    else:
        raise ValueError("Expected one of 'var' or 'std'. Got {}.".format(dispersion))
    assert L <= sample.min() and sample.max() <= U, "min={}, max={}".format(
        sample.min(), sample.max
    )
    logging.debug("calculating smooth sensitivity for %s", str(sample))
    local_sensitivities = _local_sensitivities(sample, L, U, mean, dispersion_fun)
    discounted_sensitivities = [
        loc_sens * math.exp(-beta * distance)
        for (loc_sens, distance) in local_sensitivities
    ]
    logging.debug("discounted sensitivities %s", str(discounted_sensitivities))
    return max(discounted_sensitivities)

def _local_sensitivities(
    sample: np.ndarray,
    L: float,
    U: float,
    mean: float,
    dispersion: Callable[[np.ndarray], float],
) -> Sequence[Tuple[float, int]]:
    """Compute the local sensitivities of all the relevant k-neighbors,
    for k = 0, …, n, of `sample."""
    logging.debug("calculating local sensitivities for %s", str(sample))
    sensitivities = []
    for k in range(sample.size + 1):
        wc_neighbor1 = _worst_case_k_neighbor(
            k, sample, "max_var", L, U, mean, dispersion
        )
        ls1 = _local_sensitivity(wc_neighbor1, k, L, U, mean, dispersion)
        wc_neighbor2 = _worst_case_k_neighbor(
            k, sample, "min_var", L, U, mean, dispersion
        )
        ls2 = _local_sensitivity(wc_neighbor2, k, L, U, mean, dispersion)

        sensitivities.append((max(ls1, ls2), k))
    logging.debug("local sensitivities %s", str(sensitivities))
    return sensitivities

def _worst_case_k_neighbor(
    k: int,
    sample: np.ndarray,
    mode: str,
    L: float,
    U: float,
    mean: float,
    dispersion: Callable[[np.ndarray], float],
) -> np.ndarray:
    """Compute k-neighbors which are good candidate for having the
    maximal local sensitivity among all k-neighbors of `sample`.

    Notes
    -----
    Currently only the dispersions 'var' and 'std' are supported.
    """
    logging.debug(
        "calculating worst case k-neighbor for k=%d, sample=%s, mode=%s",
        k,
        str(sample),
        mode,
    )
    if k == 0:
        return sample
    if not dispersion in [np.var, np.std]:
        raise ValueError(
            "Dispersion {} is currently not supported. Use 'var' or "
            "'std' instead".format(dispersion)
        )
    # In this case it does not matter whether I calculate var or std,
    # as both of them yield the same worst case.
    if mode == "max_var":
        max_var_indices = k_max_variance_subset_indices(k, sample)
        max_var_complement = sample[_complement(len(sample), max_var_indices)]
        # It doesn't matter where I add the new values, as the variance
        # will stay the same.
        worst_case = np.concatenate([np.tile(mean, k), max_var_complement])
    elif mode == "min_var":
        min_var_indices = k_min_variance_subset_indices(k, sample)
        min_var_complement = sample[_complement(len(sample), min_var_indices)]

        evcs = _extreme_value_combinations(k, L, U)
        evc = next(evcs)
        worst_case = current_case = np.concatenate([evc, min_var_complement])
        worst_case_var = current_var = np.var(current_case)
        current_mean = np.mean(current_case)
        n = len(sample)
        for evc in evcs:
            # Use the fact that in each iteration, an U is replaced by L
            (previous_mean, previous_var) = _mean_var_without(
                U, current_mean, current_var, n
            )
            (current_mean, current_var) = _mean_var_with(
                L, previous_mean, previous_var, n
            )
            if current_var > worst_case_var:
                worst_case_var = current_var
                worst_case = np.concatenate([evc, min_var_complement])
    else:
        raise ValueError("Unsupported mode: {}".format(mode))
    logging.debug("worst case k-neighbor %s", str(worst_case))
    return worst_case  # type: ignore

def _complement(n: int, indices: np.ndarray) -> np.ndarray:
    """Return those indices among [0, 1, ..., n - 1], which do not occur
    in `indices`."""
    return np.setdiff1d(np.arange(n), indices)  # type: ignore

def _extreme_value_combinations(k: int, L: float, U: float) -> Iterator[np.ndarray]:
    """For each value of i in [0, 1, …, `k`], yield the sequence i times
    `L`, followed by (k - i) times `U`.

    Notes
    -----
    The iterator yields always the same object, which is manipulated in
    place, rather than yielding fresh arrays.
    """
    combination = np.tile(U, k)
    yield combination
    for i in range(k):
        combination[i] = L
        yield combination

def _local_sensitivity(
    sample: np.ndarray,
    k: int,
    L: float,
    U: float,
    mean: float,
    dispersion: Callable[[np.ndarray], float],
) -> float:
    logging.debug("calculating local sensitivity for %s", str(sample))
    disp = dispersion(sample)
    sample_mean = np.mean(sample)
    n = len(sample)
    dist_from_disp = 0.0
    for i in itertools.chain((0,), range(k - 1, sample.size)):
        if dispersion is np.std:
            base_mean, base_var = _mean_var_without(
                sample[i], sample_mean, disp ** 2, n
            )
            dists = [
                abs(disp - math.sqrt(_mean_var_with(e, base_mean, base_var, n)[1]))
                for e in [L, U, mean]
            ]
        elif dispersion is np.var:
            base_mean, base_var = _mean_var_without(sample[i], sample_mean, disp, n)
            dists = [
                abs(disp - _mean_var_with(e, base_mean, base_var, n)[1])
                for e in [L, U, mean]
            ]
        else:
            raise ValueError()
        dist_from_disp = max([dist_from_disp] + dists)
    local_sens = dist_from_disp
    logging.debug("local sensitivity %f", local_sens)
    return local_sens

def _mean_var_with(
    e: float, previous_mean: float, previous_var: float, current_n: int
) -> Tuple[float, float]:
    """Calculate the sample mean and variance of a sample where `e` has
    been added to, based on the previous sample mean and variance.

    Returns
    -------
    (mean, var) : (float, float)
        The sample mean and variance of a sample where `e` has
        (implicitly) been added to.

    Notes
    -----
    Implementation based on
    https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance,
    therefore the re-evaluation of the whole sample is not needed.
    """
    new_mean = previous_mean + (e - previous_mean) / current_n
    new_var = (
        previous_var + ((e - previous_mean) * (e - new_mean) - previous_var) / current_n
    )
    if new_var < 0.0:
        raise ArithmeticError(
            "Numerical instability detected. Variance is negative. "
            "e={}, previous_mean={}, previous_var={}, current_n={}, "
            "mean={}, var={}".format(
                e, previous_mean, previous_var, current_n, new_mean, new_var
            )
        )
    return (new_mean, new_var)

def _mean_var_without(
    e: float, mean_with_e: float, var_with_e: float, n_with_e: int
) -> Tuple[float, float]:
    """Given a sample variance and a corresponding sample mean, both
    including element `e`, calculate the mean and variance of the sample
    without element `e`.

    Returns
    -------
    (mean, var) : (float, float)
        The mean an variance which remain when the effect of `e` is
        removed from `var_with_e` and `mean_with_e`.

    Notes
    -----
    This implementation is ased on
    https://stackoverflow.com/a/30876815/3389669 and therefore does not
    need to reiterate over the whole sample again.
    """
    if n_with_e < 2:
        raise ValueError("Mean and variance undefined for (to be) empty sequence.")
    mean_without_e = mean_with_e - (e - mean_with_e) / (n_with_e - 1)
    if n_with_e == 2:
        return (mean_without_e, 0.0)
    # The first summand is multiplied by `n_with_e`, because the
    # aggregate in the cited formula is just the sum of squared
    # differences, not their average.
    var_without_e = (
        var_with_e * n_with_e - (e - mean_without_e) * (e - mean_with_e)
    ) / (n_with_e - 1)
    if var_without_e < 0.0:
        logging.warning(
            "Numerical instability detected. Variance is negative. "
            "e={}, mean_with_e={}, var_with_e={}, n_with_e={}, "
            "mean={}, var={}. Setting variance to 0.0.".format(
                e, mean_with_e, var_with_e, n_with_e, mean_without_e, var_without_e
            )
        )
        return (mean_without_e, 0.0)
    else:
        return (mean_without_e, var_without_e)

def k_min_variance_subset_indices(k: int, sample: np.ndarray) -> np.ndarray:
    """
    Parameters
    ----------
    sample:
        An ascendingly sorted sequence to extract the indices of a
        k-minimal variance subset from.

    Returns:
    --------
    indices : np.ndarray
        The indices of the variance minimizing subset of `sample`.
        """
    if k == 0:
        return np.array([])
    elif k == 1:
        return np.array([0])

    n = len(sample)
    assert 0 <= k <= n
    indices = np.arange(n)
    min_var_i = 0
    min_var = current_var = np.var(sample[0:k])
    current_mean = np.mean(sample[0:k])
    for i in range(1, n - k + 1):
        (previous_mean, previous_var) = _mean_var_without(
            sample[i - 1], current_mean, current_var, k
        )
        (current_mean, current_var) = _mean_var_with(
            sample[i + k - 1], previous_mean, previous_var, k
        )
        if current_var < min_var:
            min_var = current_var
            min_var_i = i
    return cast(np.ndarray, indices[min_var_i : min_var_i + k])

def k_max_variance_subset_indices(k: int, sample: np.ndarray) -> np.ndarray:
    """
    Parameters
    ----------
    sample:
        An ascendingly sorted sequence to extract the indices of a
        k-maximal variance subset from.

    Returns:
    --------
    indices : np.ndarray
        The indices of the variance maximizing subset of `sample`.
    """
    if k == 0:
        return np.array([])
    elif k == 1:
        return np.array([0])

    n = len(sample)
    assert 0 <= k <= n
    indices = np.arange(n)
    max_var_i = 0
    max_var = current_var = np.var(sample[n - k : n])
    current_mean = np.mean(sample[n - k : n])
    for i in range(1, k + 1):
        (previous_mean, previous_var) = _mean_var_without(
            sample[n - k + i - 1], current_mean, current_var, k
        )
        (current_mean, current_var) = _mean_var_with(
            sample[i - 1], previous_mean, previous_var, k
        )
        if current_var > max_var:
            max_var = current_var
            max_var_i = i
    return cast(
        np.ndarray,
        np.concatenate([indices[0:max_var_i], indices[n - k + max_var_i : n]]),
    )

def speed_test() -> None:
    rng = np.random.default_rng(42)
    big_sample = rng.standard_normal(4500)
    s = smooth_sensitivity(big_sample, 0, 0.2, -5, 5, "std")
    print("Smooth sensitivity of {} element array: {}".format(len(big_sample), s))

if __name__ == "__main__":
    speed_test()


# /usr/bin/python3
import ipaddress
import random
from scapy.all import *
import pdb

from infra.common.logging import logger
import infra.api.api as infra_api
from apollo.config.resmgr import Resmgr
from apollo.config.store import EzAccessStore
import apollo.config.utils as utils
import apollo.config.topo as topo
from apollo.config.agent.api import ObjectTypes as ObjectTypes
import types_pb2 as types_pb2

from infra.common.objects import IpAddress
from infra.common.objects import Ipv6Address
from infra.common.objects import MacAddressBase

IPV4_HOST = ipaddress.IPv4Address(0xbadee1ba)
IPV6_HOST = ipaddress.IPv6Address('e1ba:aced:a11:face:b00c:bade:da75:900d')

def __get_packet_template_impl(obj, tsargs=None, modargs=None):
    af = obj.AddrFamily
    template = 'ETH'
    template += "_%s" % (af)
    proto = None

    # testspec args takes precedence over module args
    if tsargs is not None:
        proto = tsargs.proto
    elif modargs is not None:
        proto = __get_module_args_value(modargs, 'proto')
    else:
        assert(0)

    if af == "IPV6" and proto == "icmp":
        proto = "icmpv6"
    template += "_%s" % (proto)
    return infra_api.GetPacketTemplate(template)

def GetPacketTemplateFromMapping(testcase, packet, args=None):
    return __get_packet_template_impl(testcase.config.localmapping, args, testcase.module.args)

def __get_proto_from_policy_rule(rule):
    if rule is None:
        return "TCP"
    l3match = rule.L3Match
    if not l3match.valid:
        return "TCP"
    get_rand_l3_proto = False
    if l3match.Proto == utils.L3PROTO_MAX:
        get_rand_l3_proto = True

    return utils.GetIPProtoName(l3match.Proto, get_rand_l3_proto)

def __get_packet_template_from_policy_impl(rule, policy, protocol=None):
    if 'no-' not in policy.PolicyType:
        protocol = protocol if protocol else __get_proto_from_policy_rule(rule)
        template = 'ETH_%s_%s' % (policy.AddrFamily, protocol)
    else:
        if "no-tcp" in policy.PolicyType:
            template = 'ETH_%s_%s' % (policy.AddrFamily, 'TCP')
        elif 'no-udp' in policy.PolicyType:
            template = 'ETH_%s_%s' % (policy.AddrFamily, 'UDP')
        elif 'no-icmp' in policy.PolicyType:
            template = 'ETH_%s_%s' % (policy.AddrFamily, 'ICMP')
    return infra_api.GetPacketTemplate(template)

def GetPacketTemplateFromPolicy(testcase, packet, args=None):
    iterelem = testcase.module.iterator.Get()
    protocol = getattr(iterelem, "proto", None) if iterelem else None
    return __get_packet_template_from_policy_impl(testcase.config.tc_rule,
                                                  testcase.config.policy,
                                                  protocol=protocol)

def __get_host_from_pfx_impl(pfx, af):
    """
        if route is None / default route, returns IPV4_HOST / IPV6_HOST
        returns a random usable host otherwise
    """
    if pfx is None or utils.isDefaultRoute(pfx):
        if af == "IPV4":
            return str(IPV4_HOST)
        elif af == "IPV6":
            return str(IPV6_HOST)
        else:
            logger.error("ERROR: invalid AddrFamily ", af)
            sys.exit(1)
            return None
    total_hosts = pfx.num_addresses
    host = pfx.network_address
    if total_hosts > 1:
        host += random.randint(0, total_hosts-1)
    return str(host)

def __get_host_from_pfx(pfx, af, pos=None):
    if pfx is None or utils.isDefaultRoute(pfx) or pos is None:
        host = __get_host_from_pfx_impl(pfx, af)
    elif pos == 'right':
        #first ip post prefix range
        host = pfx.network_address + pfx.num_addresses
    elif pos == 'left':
        #first ip pre prefix range
        host = pfx.network_address - 1
    elif pos == 'first':
        #first ip in prefix range
        host = pfx.network_address
    elif pos == 'last':
        #last ip in prefix range
        host = pfx.network_address + pfx.num_addresses - 1
    return str(host)

def __get_module_args_value(modargs, attr):
    if modargs is not None:
        for args in modargs:
            if hasattr(args, attr):
                pval = getattr(args, attr, None)
                return pval
    return None

def __get_pfx_position_selector(modargs):
    return __get_module_args_value(modargs, 'prefix')

def __get_usable_pfx_from_range(ipaddrLow, ipaddrHigh, pos):
    if not all([ipaddrLow, ipaddrHigh]):
        return None
    elif pos == 'right':
        #first ip beyond range
        host = ipaddrHigh + 1
    elif pos == 'left':
        #first ip before range
        host = ipaddrLow - 1
    elif pos == 'first':
        #first ip in range
        host = ipaddrLow
    elif pos == 'last':
        #last ip in range
        host = ipaddrHigh
    else:
        # random pfx between ipaddrLow and ipaddrHigh
        pfxlist = [ipaddr for ipaddr in ipaddress.summarize_address_range(ipaddrLow, ipaddrHigh)]
        return random.choice(pfxlist)

    return ipaddress.ip_network(host)

def __get_usable_pfx_from_tag(tag, pfxpos):
    pfxlist = tag.Prefixes if tag else None
    if pfxlist is None:
        pfx = None
    else:
        pfx = random.choice(pfxlist)
    return pfx

def __get_usable_pfx_from_rule_impl(matchtype, ippfx=None, ipaddrLow=None, ipaddrHigh=None, tag=None, pfxpos=None):
    if matchtype == topo.L3MatchType.PFX:
        return ippfx
    elif matchtype == topo.L3MatchType.PFXRANGE:
        return __get_usable_pfx_from_range(ipaddrLow, ipaddrHigh, pfxpos)
    elif matchtype == topo.L3MatchType.TAG:
        return __get_usable_pfx_from_tag(tag, pfxpos)
    return None

def __get_usable_host_from_rule(rule, policy, pfxpos, testcase):
    af = policy.AddrFamily
    if rule is None:
        return __get_host_from_pfx(None, af, pfxpos)
    direction = policy.Direction
    l3match = rule.L3Match
    if not l3match.valid:
        pfx = None
    elif direction == "ingress":
        if utils.IsPipelineApulu() and testcase.config.remotemapping:
            return testcase.config.remotemapping.IP
        else:
            pfx = __get_usable_pfx_from_rule_impl(l3match.SrcType, l3match.SrcPrefix,
                                                  l3match.SrcIPLow, l3match.SrcIPHigh,
                                                  l3match.SrcTag, pfxpos)
    else:
        if utils.IsPipelineApulu() and testcase.config.remotemapping:
            return testcase.config.remotemapping.IP
        else:
            pfx = __get_usable_pfx_from_rule_impl(l3match.DstType, l3match.DstPrefix,
                                                  l3match.DstIPLow, l3match.DstIPHigh,
                                                  l3match.DstTag, pfxpos)
    return __get_host_from_pfx(pfx, af, pfxpos)

def GetUsableHostFromPolicy(testcase, packet, args=None):
    policy = testcase.config.policy
    rule = testcase.config.tc_rule
    pfxpos = __get_pfx_position_selector(testcase.module.args)
    return __get_usable_host_from_rule(rule, policy, pfxpos, testcase)

def __get_non_default_random_route(routeTable):
    routes = list(routeTable.routes.values())
    numroutes = len(routes)
    route = None
    if numroutes == 0:
        pass
    elif numroutes == 1:
        if not utils.isDefaultRoute(routes[0].ipaddr):
            route = routes[0]
    else:
        while True:
            route = random.choice(routes)
            if hasattr(route, "DstNatIp") and route.DstNatIp:
                continue
            if not utils.isDefaultRoute(route.ipaddr):
                break
    return route

def __get_dnat_route(route_tbl):
    return str(route_tbl.ServiceNatPrefix.network_address)

def __get_dnat_route_dest(route_tbl):
    return str(route_tbl.DstNatIp)

def GetUsableHostFromRoute(testcase, packet, args=None):
    route = __get_non_default_random_route(testcase.config.route)
    routepfx = route.ipaddr if route else None
    pfxpos = __get_pfx_position_selector(testcase.module.args)
    addr = __get_host_from_pfx(routepfx, testcase.config.route.AddrFamily, pfxpos)
    return addr

def GetDnatRoute(testcase, packet, args=None):
    addr = __get_dnat_route(testcase.config.route)
    logger.info("returning ", addr)
    return addr

def GetDnatRouteDest(testcase, packet, args=None):
    addr = __get_dnat_route_dest(testcase.config.route)
    logger.info("returning ", addr)
    return addr

def __get_random_port_in_range(beg=utils.L4PORT_MIN, end=utils.L4PORT_MAX):
    return random.randint(beg, end)

def __get_valid_port(port):
    if port < utils.L4PORT_MIN:
        return utils.L4PORT_MIN
    elif port > utils.L4PORT_MAX:
        return utils.L4PORT_MAX
    else:
        return port

def __get_port_from_rule(rule, pos=None, isSource=True):
    l4matchobj = rule.L4Match if rule else None
    if l4matchobj is None or l4matchobj.valid == False:
        return __get_random_port_in_range()
    if isSource:
        beg = l4matchobj.SportLow
        end = l4matchobj.SportHigh
    else:
        beg = l4matchobj.DportLow
        end = l4matchobj.DportHigh
    if pos == 'right':
        port = end + 1
    elif pos == 'left':
        port = beg - 1
    elif pos == 'first':
        port = beg
    elif pos == 'last':
        port = end
    else:
        return __get_random_port_in_range(beg, end)
    return __get_valid_port(port)

def GetUsableSPortFromPolicy(testcase, packet, args=None):
    rule = testcase.config.tc_rule
    pos = __get_module_args_value(testcase.module.args, 'sport')
    return __get_port_from_rule(rule, pos)

def GetUsableDPortFromPolicy(testcase, packet, args=None):
    rule = testcase.config.tc_rule
    pos = __get_module_args_value(testcase.module.args, 'dport')
    return __get_port_from_rule(rule, pos, False)

def __get_random_icmptype_in_range(beg=utils.ICMPTYPE_MIN, end=utils.ICMPTYPE_MAX):
    return random.randint(beg, end)

def __get_valid_icmptype(icmptype):
    if icmptype < utils.ICMPTYPE_MIN:
        return utils.ICMPTYPE_MIN
    elif icmptype > utils.ICMPTYPE_MAX:
        return utils.ICMPTYPE_MAX
    else:
        return icmptype

def __get_icmp_values_from_rule(rule, pos=None, isIcmpCode=False):
    l4matchobj = rule.L4Match if rule else None
    if l4matchobj is None or l4matchobj.valid == False or \
       l4matchobj.IcmpType == types_pb2.MATCH_ANY:
        return __get_random_icmptype_in_range()
    if isIcmpCode:
        val = l4matchobj.IcmpCode
    else:
        val = l4matchobj.IcmpType
    if pos == 'right':
        val = val + 1
    elif pos == 'left':
        val = val - 1
    return __get_valid_icmptype(val)

def GetUsableICMPTypeFromPolicy(testcase, packet, args=None):
    rule = testcase.config.tc_rule
    pos = __get_module_args_value(testcase.module.args, 'icmptype')
    return __get_icmp_values_from_rule(rule, pos)

def GetUsableICMPCodeFromPolicy(testcase, packet, args=None):
    rule = testcase.config.tc_rule
    pos = __get_module_args_value(testcase.module.args, 'icmpcode')
    return __get_icmp_values_from_rule(rule, pos, True)

def __is_matching_ip(matchtype, ipaddr, ippfx=None, ipaddrLow=None, ipaddrHigh=None, tag=None):
    ipaddr = ipaddress.ip_address(ipaddr)
    if matchtype == topo.L3MatchType.PFX:
        if ippfx is None:
            return True
        return ipaddr in ippfx
    elif matchtype == topo.L3MatchType.PFXRANGE:
        if not all([ipaddrLow, ipaddrHigh]):
            return True
        return ((ipaddr >= ipaddrLow) and (ipaddr <= ipaddrHigh))
    elif matchtype == topo.L3MatchType.TAG:
        tagpfxlist = tag.Prefixes if tag else None
        if tagpfxlist is None:
            return False
        for tagpfx in tagpfxlist:
            if ipaddr in tagpfx:
                return True
    return False

def __is_matching_src_ip(src_ip, l3matchobj, direction, testcase):
    af = testcase.config.localmapping.AddrFamily
    if utils.IsPipelineApulu() and (l3matchobj.SrcType == topo.L3MatchType.TAG):
        if direction == "ingress":
            if af == 'IPV4':
                for obj in testcase.config.v4rtags[l3matchobj.SrcTag]:
                    if str(src_ip) == obj.IP:
                        return True
            else:
                for obj in testcase.config.v6rtags[l3matchobj.SrcTag]:
                    if str(src_ip) == obj.IP:
                        return True
        else:
            if af == 'IPV4':
                for obj in testcase.config.v4ltags[l3matchobj.SrcTag]:
                    if str(src_ip) == obj.IP:
                        return True
            else:
                for obj in testcase.config.v6ltags[l3matchobj.SrcTag]:
                    if str(src_ip) == obj.IP:
                        return True
        return False
    return __is_matching_ip(l3matchobj.SrcType, src_ip, l3matchobj.SrcPrefix, l3matchobj.SrcIPLow, l3matchobj.SrcIPHigh, l3matchobj.SrcTag)

def __is_matching_dst_ip(dst_ip, l3matchobj, direction, testcase):
    af = testcase.config.localmapping.AddrFamily
    if utils.IsPipelineApulu() and (l3matchobj.DstType == topo.L3MatchType.TAG):
        if direction == "ingress":
            if af == 'IPV4':
                for obj in testcase.config.v4ltags[l3matchobj.DstTag]:
                    if str(dst_ip) == obj.IP:
                        return True
            else:
                for obj in testcase.config.v6ltags[l3matchobj.DstTag]:
                    if str(dst_ip) == obj.IP:
                        return True
        else:
            if af == 'IPV4':
                for obj in testcase.config.v4rtags[l3matchobj.DstTag]:
                    if str(dst_ip) == obj.IP:
                        return True
            else:
                for obj in testcase.config.v6rtags[l3matchobj.DstTag]:
                    if str(dst_ip) == obj.IP:
                        return True
        return False
    return __is_matching_ip(l3matchobj.DstType, dst_ip, l3matchobj.DstPrefix, l3matchobj.DstIPLow, l3matchobj.DstIPHigh, l3matchobj.DstTag)

def __is_matching_proto(proto1, proto2):
    if proto2 == 0 or proto2 == utils.L3PROTO_MAX:
        # wildcard
        return True
    return proto1 == proto2

def __is_matching_L4port(port, portLow=utils.L4PORT_MIN, portHigh=utils.L4PORT_MAX):
    return ((port >= portLow) and (port <= portHigh))

def __is_matching_sport(sport, l4matchobj):
    return __is_matching_L4port(sport, l4matchobj.SportLow, l4matchobj.SportHigh)

def __is_matching_dport(dport, l4matchobj):
    return __is_matching_L4port(dport, l4matchobj.DportLow, l4matchobj.DportHigh)

def __is_matching_icmptype(icmptype, l4matchobj):
    return ((icmptype == l4matchobj.IcmpType) or
            (l4matchobj.IcmpType == types_pb2.MATCH_ANY))

def __is_matching_icmpcode(icmpcode, l4matchobj):
    return ((icmpcode == l4matchobj.IcmpCode) or
            (l4matchobj.IcmpCode == types_pb2.MATCH_ANY))

def __is_l3_match(packet_tuples, l3matchobj, direction, testcase):
    if not l3matchobj.valid:
        return True
    if not __is_matching_src_ip(packet_tuples[0], l3matchobj, direction, testcase):
        logger.verbose("l3match sip fail")
        return False
    if not __is_matching_dst_ip(packet_tuples[1], l3matchobj, direction, testcase):
        logger.verbose("l3match dip fail")
        return False
    if not __is_matching_proto(packet_tuples[2], l3matchobj.Proto):
        logger.verbose("l3match proto fail")
        return False
    return True

def __is_l4_match(packet_tuples, l4matchobj):
    if not l4matchobj.valid:
        return True
    if utils.IsICMPProtocol(packet_tuples[2]):
        if not __is_matching_icmptype(packet_tuples[5], l4matchobj):
            logger.verbose("l4match icmp type fail")
            return False
        if not __is_matching_icmpcode(packet_tuples[6], l4matchobj):
            logger.verbose("l4match icmp code fail")
            return False
    else:
        if not __is_matching_sport(packet_tuples[3], l4matchobj):
            logger.verbose("l4match sport fail")
            return False
        if not __is_matching_dport(packet_tuples[4], l4matchobj):
            logger.verbose("l4match dport fail")
            return False
    return True

def __is_matching_rule(packet_tuples, rule, direction, testcase):
    if not __is_l3_match(packet_tuples, rule.L3Match, direction, testcase):
        logger.verbose("l3match fail")
        return False
    if not __is_l4_match(packet_tuples, rule.L4Match):
        logger.verbose("l4match fail")
        return False
    return True

def __get_packet_tuples(pkt):
    payload = None
    sip = None
    dip = None
    proto = 0
    sport = 0
    dport = 0
    icmptype = 0
    icmpcode = 0
    logger.info("Actual Packet ", pkt.summary())
    if IP in pkt:
        sip = pkt[IP].src
        dip = pkt[IP].dst
        proto = pkt[IP].proto
    elif IPv6 in pkt:
        sip = pkt[IPv6].src
        dip = pkt[IPv6].dst
        proto = pkt[IPv6].nh
        payload = pkt[IPv6].payload
    if TCP in pkt:
        sport = pkt[TCP].sport
        dport = pkt[TCP].dport
    elif UDP in pkt:
        sport = pkt[UDP].sport
        dport = pkt[UDP].dport
    elif ICMP in pkt:
        icmptype = pkt[ICMP].type
        icmpcode = pkt[ICMP].code
    elif proto == 58: #ICMPv6
        icmptype = payload.type
        icmpcode = payload.code
    #TODO: make packet_tuples a class
    packet_tuples = [sip, dip, proto, sport, dport, icmptype, icmpcode]
    logger.info("Retrieved Packet Tuples ", packet_tuples)
    return packet_tuples

def __rule_dump(tc_rule, match_rule):
    logger.info("TestCase rule for packet ")
    if tc_rule == None:
        logger.info("No Rule ")
    else:
        tc_rule.Show()
    if match_rule:
        logger.info("Final matching rule for packet")
        match_rule.Show()

def __get_final_result(tc_rule, match_rule, testcase):
    policy = testcase.config.policy
    securityprofile = testcase.config.securityprofile
    iterelem = testcase.module.iterator.Get()
    result = getattr(iterelem, "result", None) if iterelem else None
    if result:
        final_result = utils.GetRpcSecurityRuleAction(result)
        logger.info(f"Matching to overrided result action: {final_result}")
        return final_result
    elif match_rule:
        final_result = match_rule.Action
        logger.info(f"Matching to rule action: {final_result}")
    elif policy.DefaultFWAction != "none":
        final_result = utils.GetRpcSecurityRuleAction(policy.DefaultFWAction)
        logger.info(f"Matching to policy default action: {final_result}")
    elif not securityprofile:
        final_result = types_pb2.SECURITY_RULE_ACTION_DENY
        logger.info(f"security profile not configured, Matching to action: {final_result}")
    elif securityprofile.DefaultFWAction != types_pb2.SECURITY_RULE_ACTION_NONE:
        final_result = securityprofile.DefaultFWAction
        logger.info(f"Matching to security profile default action: {final_result}")
    else:
        final_result = types_pb2.SECURITY_RULE_ACTION_DENY
        logger.info(f"Matching to default action: {final_result}")
    __rule_dump(tc_rule, match_rule)
    return final_result

def __match_and_get_final_result(policies, pkt, direction, testcase):
    packet_tuples = __get_packet_tuples(pkt)
    tc_rule = testcase.config.tc_rule
    rules = []
    for policyid in policies:
        policyobj = __get_config_object(ObjectTypes.POLICY, policyid)
        rules = sorted(policyobj.rules, key=lambda x: x.Priority)
        match_rule = None
        for rule in rules:
            if __is_matching_rule(packet_tuples, rule, direction, testcase):
                match_rule = rule
                break
        final_result = __get_final_result(tc_rule, match_rule, testcase)
        if final_result is types_pb2.SECURITY_RULE_ACTION_DENY:
            break
    __rule_dump(tc_rule, match_rule)
    return final_result

def __get_matching_rule(policies, pkt, direction, testcase):
    packet_tuples = __get_packet_tuples(pkt)
    rules = []
    for policyid in policies:
        policyobj = __get_config_object(ObjectTypes.POLICY, policyid)
        rules.extend(policyobj.rules)
    # Get a new copy of stable sorted (based on priority) rules
    # Note: lower the value of rule.Priority higher the Priority
    rules = sorted(rules, key=lambda x: x.Priority)
    match_rule = None
    for rule in rules:
        if __is_matching_rule(packet_tuples, rule, direction, testcase):
            # if priorities are same for multiple rules, choose the first
            match_rule = rule
            break
    return match_rule

def __get_security_policies_from_lmapping(lmapping, direction):
    attr = ""
    attr += "Ing" if direction == "ingress" else "Eg"
    attr += "V4" if lmapping.IsV4() else "V6"
    attr += "SecurityPolicyIds"
    vnic = lmapping.VNIC
    subnet = vnic.SUBNET
    policies = []
    # TODO: order in which they applied
    policies.extend(getattr(vnic, attr))
    policies.extend(getattr(subnet, attr))
    return policies

def GetExpectedCPSPacket(testcase, args):
    device = testcase.config.devicecfg
    tc_rule = testcase.config.tc_rule
    # get nacl which we selected for testing
    policy = testcase.config.policy
    # get all security policies which needs to be applied
    policies = __get_security_policies_from_lmapping(testcase.config.localmapping, policy.Direction)
    pkt = testcase.packets.Get(args.ipkt).GetScapyPacket()
    if device.PolicyAnyDeny:
        final_result = __match_and_get_final_result(policies, pkt, policy.Direction, testcase)
    else:
        match_rule = __get_matching_rule(policies, pkt, policy.Direction, testcase)
        final_result = __get_final_result(tc_rule, match_rule, testcase)
    if final_result == types_pb2.SECURITY_RULE_ACTION_DENY:
        logger.info("GetExpectedCPSPacket: packet denied")
        return None
    logger.info("GetExpectedCPSPacket: packet allowed")
    return testcase.packets.Get(args.epkt_pass)

def IsCPSPacketExpected(testcase, args):
    pkt = GetExpectedCPSPacket(testcase, args)
    return True if pkt is None else False

def GetInvalidMPLSTag(testcase, packet, args=None):
    return next(Resmgr.InvalidMplsSlotIdAllocator)

def GetInvalidVnid(testcase, packet, args=None):
    return next(Resmgr.InvalidVxlanIdAllocator)

def __get_packet_encap_type_impl(obj, args):
    if obj.IsWorkload():
        return 'ENCAP_MPLS2'
    elif obj.IsIgw():
        return 'ENCAP_MPLS'
    else:
        return None

def __get_packet_encap_impl(obj, tunnel, args):
    if obj.IsEncapTypeVXLAN():
        encap = 'ENCAP_VXLAN'
    elif obj.IsEncapTypeMPLS():
        encap = __get_packet_encap_type_impl(tunnel, args)
    else:
        assert 0
    return infra_api.GetPacketTemplate(encap)

# This can be called for packets to switch or from switch
def GetPacketEncapFromMapping(testcase, packet, args=None):
    encaps = []
    encaps.append(__get_packet_encap_impl(testcase.config.devicecfg, testcase.config.tunnel, args))
    return encaps

def __get_host_packet_encap_impl(vnic):
    encaps = []
    if vnic.IsEncapTypeVLAN():
        vlan_encap = infra_api.GetPacketTemplate('ENCAP_QTAG')
        encaps.append(vlan_encap)
    return encaps

# Encap for to/from host packet
def GetHostPacketEncapFromVnic(testcase, packet, args=None):
    if args is None:
        vnic = testcase.config.localmapping.VNIC
    else:
        vnic = testcase.config.remotemapping.VNIC
    return __get_host_packet_encap_impl(vnic)

def __get_packet_srcmac(fwdmode, robj, lobj):
    local_subnet = lobj.VNIC.SUBNET
    if fwdmode == 'L2L':
        remote_subnet = robj.VNIC.SUBNET
        if local_subnet.SubnetId != remote_subnet.SubnetId:
            return robj.VNIC.SUBNET.VirtualRouterMACAddr
        else:
            return lobj.VNIC.MACAddr
    elif fwdmode == 'L2R':
        remote_subnet = robj.SUBNET
        if local_subnet.SubnetId != remote_subnet.SubnetId:
            return robj.SUBNET.VirtualRouterMACAddr
        else:
            return lobj.VNIC.MACAddr
    elif fwdmode == 'R2L':
        remote_subnet = robj.SUBNET
        if local_subnet.SubnetId != remote_subnet.SubnetId:
            return lobj.VNIC.SUBNET.VirtualRouterMACAddr
        else:
            return robj.VNIC.MACAddr
    elif fwdmode == 'L2N':
        return lobj.VNIC.SUBNET.VPC.VirtualRouterMACAddr
    elif fwdmode == 'N2L':
        return lobj.VNIC.SUBNET.VirtualRouterMACAddr

def GetPacketSrcMacFromMapping(tc, packet, args=None):
    return __get_packet_srcmac(args.Fwdmode,
            tc.config.remotemapping,
            tc.config.localmapping)

def __get_packet_srcmac_impl(fwdmode, dobj, robj, lobj, args):
    if dobj.IsEncapTypeMPLS():
        if fwdmode == 'L2':
            return robj.MACAddr
        elif fwdmode == 'L2L':
            return robj.VNIC.SUBNET.VirtualRouterMACAddr
        else:
            return lobj.VNIC.SUBNET.VirtualRouterMACAddr
    elif dobj.IsEncapTypeVXLAN():
        if fwdmode == 'L2L':
            return robj.VNIC.SUBNET.VirtualRouterMACAddr
        else:
            return lobj.VNIC.SUBNET.VirtualRouterMACAddr
    else:
        assert 0

# This can be called for packets to host from switch
def GetPacketSrcMacAddrFromMapping(testcase, packet, args=None):
    return __get_packet_srcmac_impl(testcase.config.root.FwdMode,
            testcase.config.devicecfg, testcase.config.remotemapping,
            testcase.config.localmapping, args)

def __get_packet_ttl(fwdmode, robj, lobj, ttl):
    local_subnet = lobj.VNIC.SUBNET
    if fwdmode == 'L2L':
        remote_subnet = robj.VNIC.SUBNET
        if local_subnet.SubnetId != remote_subnet.SubnetId:
            return ttl - 1
        else:
            return ttl
    elif fwdmode == 'L2R':
        remote_subnet = robj.SUBNET
        if local_subnet.SubnetId != remote_subnet.SubnetId:
            return ttl - 1
        else:
            return ttl
    elif fwdmode == 'R2L':
        remote_subnet = robj.SUBNET
        if local_subnet.SubnetId != remote_subnet.SubnetId:
            return ttl - 1
        else:
            return ttl
    elif fwdmode == 'L2N' or fwdmode == 'N2L':
        return ttl - 1

def GetPacketTtl(tc, packet, args=None):
    pkt = tc.packets.Get(args.ipkt).GetScapyPacket()
    if IP in pkt:
        ttl = pkt[IP].ttl
    else:
        assert 0
    return __get_packet_ttl(args.Fwdmode,
            tc.config.remotemapping,
            tc.config.localmapping, ttl)

def GetUsableICMPId(tc, packet, args=None):
    if GetUsableICMPId.count < 65535 and GetUsableICMPId.count >= 10000:
        GetUsableICMPId.count += 1
    else:
        GetUsableICMPId.count = 10000
    return GetUsableICMPId.count
GetUsableICMPId.count = 10000

def GetTunnelIPFromMapping(testcase, packet, args=None):
    return str(testcase.config.remotemapping.TUNNEL.RemoteIPAddr)

def __get_config_object(objtype, id):
    dutNode = EzAccessStore.GetDUTNode()
    objClient = EzAccessStore.GetConfigClient(objtype)
    return objClient.GetObjectByKey(dutNode, id)

def __getTunObject(nh_type, id):
    if nh_type == "tep":
        objtype = ObjectTypes.TUNNEL
    else:
        #TODO: Handle nh, nhgroup, vpc_peer
        assert(0)
    return __get_config_object(objtype, id)

def __get_matching_route(routetbl, addr):
    addr = ipaddress.ip_address(addr)
    matching_route = None
    matching_masklen = 0
    if routetbl.PriorityType:
        min_priority = topo.MIN_ROUTE_PRIORITY + 1
        routes = routetbl.routes
        for route in routes.values():
            if route.Priority == 0: assert 0
            if addr in route.ipaddr:
                if route.Priority  < min_priority or \
                  (route.Priority == min_priority and \
                   route.ipaddr.prefixlen > matching_masklen):
                    matching_masklen = route.ipaddr.prefixlen
                    matching_route = route
                    min_priority = route.Priority
    else:
        #TODO: move everything to above model
        assert(0)
    return matching_route

def __get_matching_route_tep(routetbl, addr):
    matching_route = __get_matching_route(routetbl, addr)
    logger.info(f"Testcase Matching route - {matching_route}")
    nh_type = matching_route.NextHopType
    if nh_type == "tep":
        nh_objid = matching_route.TunnelId
    elif nh_type == "nexthop":
        nh_objid = matching_route.NexthopId
    elif nh_type == "nhg":
        nh_objid = matching_route.NexthopGroupId
    elif nh_type == "vpcpeer":
        nh_objid = matching_route.PeerVPCId
    return __getTunObject(nh_type, nh_objid)

def __get_tunnel_from_route(tc, args):
    routetbl = tc.config.route
    pktid = getattr(args, 'ipkt', None)
    if pktid and routetbl.PriorityType:
        ipkt = tc.packets.Get(pktid).GetScapyPacket()
        packet_tuples = __get_packet_tuples(ipkt)
        addr = packet_tuples[1] if args.direction == 'TX' else packet_tuples[0]
        tep = __get_matching_route_tep(routetbl, addr)
    else:
        tep = routetbl.TUNNEL
    return tep

def GetTunnelIPFromRoute(tc, pkt, args=None):
    tep = __get_tunnel_from_route(tc, args)
    tc.config.tunnel = tep
    return str(tep.RemoteIPAddr)

def GetTunnelMacFromRoute(tc, pkt, args=None):
    tep = __get_tunnel_from_route(tc, args)
    return tep.MACAddr

def GetUplinkPortMacFromRoute(tc, pkt, args=None):
    tep = __get_tunnel_from_route(tc, args)
    if tep.IsUnderlay():
        nh = tep.NEXTHOP
    l3if = nh.L3Interface
    mac = l3if.GetInterfaceMac().get()
    return mac

def GetUnderlayRemoteMacFromRoute(tc, pkt, args=None):
    tep = __get_tunnel_from_route(tc, args)
    if tep.IsUnderlay():
        nh = tep.NEXTHOP
    else:
        assert(0)
    return nh.underlayMACAddr.get()

def GetVirtualRouterIP(testcase, pkt, args=None):
    if testcase.config.root.FwdMode == 'L3':
        if testcase.config.remotemapping.IsV6():
            return str(testcase.config.remotemapping.SUBNET.VirtualRouterIPAddr[0].packed)
        else:
            return str(testcase.config.remotemapping.SUBNET.VirtualRouterIPAddr[1])
    else:
        if testcase.config.localmapping.IsV6():
            return str(testcase.config.localmapping.VNIC.SUBNET.VirtualRouterIPAddr[0].packed)
        else:
            return str(testcase.config.localmapping.VNIC.SUBNET.VirtualRouterIPAddr[1])

def GetVirtualRouterMAC(testcase, pkt, args=None):
    if testcase.config.root.FwdMode == 'L3':
            return str(testcase.config.remotemapping.SUBNET.VirtualRouterMACAddr)
    return str(testcase.config.localmapping.VNIC.SUBNET.VirtualRouterMACAddr)

def __is_any_cfg_deleted(tc):
    device = tc.config.devicecfg
    lmapping = tc.config.localmapping
    rmapping = tc.config.remotemapping
    vnic = lmapping.VNIC
    subnet = vnic.SUBNET
    vpc = subnet.VPC
    routetable = tc.config.route
    if device.IsOverlayRoutingEnabled():
        objs = [device, vpc, subnet]
    else:
        objs = [device, vpc, routetable, subnet, vnic, lmapping, rmapping]
    values = list(map(lambda x: not(x.IsHwHabitant()) or (x.IsDirty()), list(filter(None, objs))))
    if any(values):
        return True
    return False

def GetExpectedPacket(testcase, args):
    #add all the cases for checking packet and return expected packet on demand
    if __is_any_cfg_deleted(testcase):
        logger.info("one or more cfgs deleted - no packet expected")
        return None
    return testcase.packets.Get(args.pkt)

def IsARPProxyNegativeTestCase(testcase):
    if "IPV4_ARP_PROXY_OUTSIDE_SUBNET" == testcase.module.name or \
        "IPV4_ARP_PROXY_NO_MAPPING" == testcase.module.name:
        logger.info("Negative test cases for ARP proxy")
        return True
    return False

def IsVRIPNegativeTestCase(tc):
    proto = __get_module_args_value(tc.module.args, 'proto')
    if proto == 'TCP' or proto == 'UDP':
        return True
    device = tc.config.devicecfg
    lmapping = tc.config.localmapping
    vnic = lmapping.VNIC
    subnet = vnic.SUBNET
    vpc = subnet.VPC
    if device.IsOverlayRoutingEnabled():
        objs = [device, vpc, subnet]
    else:
        objs = [device, vpc, subnet, vnic, lmapping ]
    values = list(map(lambda x: not(x.IsHwHabitant()) or (x.IsDirty()), list(filter(None, objs))))
    if any(values):
        return True
    return False

# used to check if packets are expected on the lif queue
# True  - no packets expected
# False - descriptor on expect is valid
def IsNegativeTestCase(testcase):
    # if no packet is expected, return True otherwise False
    if __is_any_cfg_deleted(testcase):
        logger.info("one or more cfgs deleted - negative testcase")
        return True
    return False

def __is_nat_enabled(routeTable):
    return routeTable.IsNatEnabled()

def __get_ip_localmapping_impl(localmapping, natEnabled):
    if natEnabled:
        publicIP = getattr(localmapping, "PublicIP", None)
        if publicIP:
            return localmapping.PublicIP
    return localmapping.IP

def GetIPFromLocalMapping(testcase, packet, args=None):
    natEnabled = __is_nat_enabled(testcase.config.route)
    return __get_ip_localmapping_impl(testcase.config.localmapping, natEnabled)

def __get_expected_nexthop(cfg):
    tunnel = cfg.tunnel
    nh = None
    if tunnel is None:
        pass
    elif tunnel.IsUnderlay():
        nh = tunnel.NEXTHOP
    return nh

def GetExpectedEgressUplinkPort(testcase, args=None):
    if testcase.config.devicecfg.IsOverlayRoutingEnabled():
        return [topo.Ports.UPLINK_0, topo.Ports.UPLINK_1]
    nh = __get_expected_nexthop(testcase.config)
    if nh is None:
        return None
    l3if = nh.L3Interface
    port = topo.EthIfIdx2Port(l3if.GetInterfaceEthIfIndex())
    return port

def GetUplinkPortMac(testcase, args=None):
    nh = __get_expected_nexthop(testcase.config)
    if nh is None:
        return None
    l3if = nh.L3Interface
    mac = l3if.GetInterfaceMac().get()
    return mac

def GetUnderlayRemoteMac(testcase, args=None):
    nh = __get_expected_nexthop(testcase.config)
    if nh is None:
        return None
    mac = nh.underlayMACAddr.get()
    return mac

def GetRingFromMapping(testcase, args=None):
    if args.type == "remote":
        hostIf = testcase.config.remotemapping.VNIC.SUBNET.HostIf
    else:
        hostIf = testcase.config.localmapping.VNIC.SUBNET.HostIf
    return hostIf.lif.GetQt(args.qid)

def GetSrcMacInArpReply(testcase, packet, args):
    if args.type == "subnet":
        return testcase.config.localmapping.VNIC.SUBNET.VirtualRouterMACAddr
    else:
        return testcase.config.remotemapping.MACAddr

def GetEncapForARP(testcase, packet):
    vlan_encap = None
    if testcase.config.localmapping.VNIC.Dot1Qenabled:
        vlan_encap = infra_api.GetPacketTemplate('ENCAP_QTAG')
    elif testcase.config.localmapping.VNIC.QinQenabled:
        vlan_encap = infra_api.GetPacketTemplate('ENCAP_QINQ')
    return [vlan_encap] if vlan_encap else []

def __get_type_val(modargs):
    return __get_module_args_value(modargs, "type")

def GetDstIpForARP(testcase, packet, args=None):
    if "IPV4_ARP_PROXY_NO_MAPPING" == testcase.module.name:
        # select a dst ip which is not in mapping table
        return testcase.config.devicecfg.IP
    elif "IPV4_ARP_PROXY_OUTSIDE_SUBNET" == testcase.module.name:
        pfx = testcase.config.localmapping.VNIC.SUBNET.IPPrefix[1]
        nextpfx = str(pfx.broadcast_address + 2)
        return nextpfx
    else:
        return testcase.config.remotemapping.IP

def GetDstMacForARP(testcase, packet, args=None):
    if "IPV4_ARP_PROXY_NO_MAPPING" == testcase.module.name:
        return testcase.config.devicecfg.MACAddr
    elif "IPV4_ARP_PROXY_OUTSIDE_SUBNET" == testcase.module.name:
        return "00:00:00:00:00:00"
    else:
        return testcase.config.remotemapping.MACAddr

def GetDstMac(testcase, packet, args=None):
    lobj = testcase.config.localmapping
    robj = testcase.config.remotemapping
    fwdmode = testcase.config.root.FwdMode
    if testcase.config.devicecfg.BridgingEnabled:
        if fwdmode == 'L2L':
            if lobj.VNIC.SUBNET.SubnetId != robj.VNIC.SUBNET.SubnetId:
                if args.direction == 'TX':
                    return robj.VNIC.SUBNET.VirtualRouterMACAddr
            return robj.VNIC.MACAddr
        else:
            if lobj.VNIC.SUBNET.SubnetId != robj.SUBNET.SubnetId:
                if args.direction == 'TX':
                    return lobj.VNIC.SUBNET.VirtualRouterMACAddr
                elif args.direction == 'RX':
                    return robj.SUBNET.VirtualRouterMACAddr
                else:
                    logger.error("Direction not provided")
                    assert(0)
            return robj.MACAddr
    else:
        if fwdmode == 'L2L':
            return robj.VNIC.MACAddr
        else:
            return robj.MACAddr

def GetSrcMac(testcase, packet, args=None):
    lobj = testcase.config.localmapping
    robj = testcase.config.remotemapping
    fwdmode = testcase.config.root.FwdMode
    if testcase.config.devicecfg.BridgingEnabled:
        if lobj.VNIC.SUBNET.SubnetId != robj.SUBNET.SubnetId:
            return lobj.VNIC.SUBNET.VirtualRouterMACAddr
    return lobj.VNIC.MACAddr

def GetVNId(testcase, args=None):
    if utils.IsBridgingEnabled(testcase.config.localmapping.Node):
        return testcase.config.localmapping.VNIC.SUBNET.Vnid
    else:
        return testcase.config.localmapping.VNIC.SUBNET.VPC.Vnid

def GetMulticastIP(testcase, packet, args=None):
    iterelem = testcase.module.iterator.Get()
    lobj = testcase.config.localmapping
    robj = testcase.config.remotemapping
    if args and args.direction == "TX":
        isV4Stack = True if lobj.AddrFamily == "IPV4" else False
    else:
        isV4Stack = True if robj.AddrFamily == "IPV4" else False

    if isV4Stack:
        if iterelem.id == 'IGMP-TO-CPU':
            return IpAddress(string='224.0.0.1')
        elif iterelem.id == 'IGMP':
            return IpAddress(string='224.0.0.22')
        else:
            return IpAddress(string='239.1.1.1')
    else:
        if iterelem.id == 'MLD_QUERY':
            return Ipv6Address(string='FF02::1')
        elif iterelem.id == 'MLD_RESPONSE':
            return Ipv6Address(string='FF02::16')
        elif iterelem.id == 'MLD_DONE':
            return Ipv6Address(string='FF02::2')
        elif iterelem.id == 'ROUTER_SOLICITATION':
            return Ipv6Address(string='FF02::2')
        elif iterelem.id == 'ROUTER_ADVERTISEMENT':
            return Ipv6Address(string='FF02::1')
        elif iterelem.id == 'NEIGHBOR_SOLICITATION':
            return Ipv6Address(string='ff08::1')
        elif iterelem.id == 'NEIGHBOR_ADVERTISEMENT':
            return Ipv6Address(string='FF02::1')
        else:
            return Ipv6Address(string='ff08::1')

def GetMulticastMacFromIPv4(ip):
    return MacAddressBase(integer=(0x01005E000000 | (ip.getnum() & 0x007FFFFF)))

def GetMulticastMacFromIPv6(ip):
    return MacAddressBase(integer=(0x333300000000 | (ip.getnum() & 0xFFFFFFFF)))

def GetMulticastMacFromIP(testcase, packet):
    ip = GetMulticastIP(testcase, packet)
    if ip.value.version == utils.IP_VERSION_4:
        return GetMulticastMacFromIPv4(ip)
    elif ip.value.version == utils.IP_VERSION_6:
        return GetMulticastMacFromIPv6(ip)
    else:
        assert(0)


"""Add pessoa to endereco.

Revision ID: 80a3e11a36f0
Revises: e932b46e6eb1
Create Date: 2023-04-24 16:58:42.044811

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '80a3e11a36f0'
down_revision = 'e932b46e6eb1'
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    with op.batch_alter_table('endereco', schema=None) as batch_op:
        batch_op.add_column(sa.Column('pessoa_id', sa.Integer(), nullable=True))
        batch_op.create_foreign_key(None, 'pessoa', ['pessoa_id'], ['id'])

    with op.batch_alter_table('pessoa', schema=None) as batch_op:
        batch_op.add_column(sa.Column('senha', sa.String(), nullable=False))

    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    with op.batch_alter_table('pessoa', schema=None) as batch_op:
        batch_op.drop_column('senha')

    with op.batch_alter_table('endereco', schema=None) as batch_op:
        batch_op.drop_constraint(None, type_='foreignkey')
        batch_op.drop_column('pessoa_id')

    # ### end Alembic commands ###


from datetime import datetime, timedelta

from django.conf import settings

import jwt

from olympia.api.models import SYMMETRIC_JWT_TYPE, APIKey

class JWTAuthKeyTester:
    def create_api_key(
        self,
        user,
        key='some-user-key',
        is_active=True,
        secret='some-shared-secret',
        **kw,
    ):
        return APIKey.objects.create(
            type=SYMMETRIC_JWT_TYPE,
            user=user,
            key=key,
            secret=secret,
            is_active=is_active,
            **kw,
        )

    def auth_token_payload(self, user, issuer, issued_at=None):
        """Creates a JWT payload as a client would."""
        issued_at = datetime.utcnow()
        return {
            # The JWT issuer must match the 'key' field of APIKey
            'iss': issuer,
            'iat': issued_at,
            'exp': issued_at
            + timedelta(seconds=settings.MAX_APIKEY_JWT_AUTH_TOKEN_LIFETIME),
        }

    def encode_token_payload(self, payload, secret):
        """Encodes a JWT payload as a client would."""
        token = jwt.encode(payload, secret, settings.JWT_AUTH['JWT_ALGORITHM'])
        return token

    def create_auth_token(self, user, issuer, secret):
        payload = self.auth_token_payload(user, issuer)
        return self.encode_token_payload(payload, secret)


from selenium import webdriver
from selenium.webdriver.firefox.service import Service
from webdriver_manager.firefox import GeckoDriverManager
from secret.secret import gh_token, user_agent
import os
import time

os.environ['GH_TOKEN'] = gh_token
url = 'https://intoli.com/blog/not-possible-to-block-chrome-headless/chrome-headless-test.html'
options = webdriver.FirefoxOptions()
options.set_preference('general.useragent.override', user_agent)

# Disable webdriver mode
options.set_preference('dom.webdriver.enabled', False)

driver = webdriver.Firefox(
    service=Service(GeckoDriverManager().install()),
    options=options
)

try:
    driver.get(url=url)
    time.sleep(10)
except Exception as ex:
    print(ex)
finally:
    driver.close()
    driver.quit()


from tkinter import *

window = Tk()
window.title("Distance Convertor")
window.minsize(height=150, width=400)
window.config(padx=100, pady=20)

def clicked():
    miles = input.get()
    mile = int(miles)
    km = round(mile * 1.6)
    label4.config(text=km)

label1 = Label(text=" ")
label1.grid(row=0, column=0)

input = Entry()
input.grid(row=0, column=2)

label2 = Label(text="Miles")
label2.grid(row=0, column=3)
label2.config(padx=60, pady=20)

label3 = Label(text="is equal to")
label3.grid(row=1, column=0)
label3.config(padx=30)

label4 = Label(text="0")
label4.grid(row=1, column=2)

label5 = Label(text="Km")
label5.grid(row=1, column=3)

button = Button(text="Calculate", command=clicked)
button.grid(row=2, column=2)

window.mainloop()


"""
Contains functions to bundle python dependencies.
"""
import logging
import typing

import lambda_bundler.dependencies as dependencies
import lambda_bundler.util as util

LOGGER = logging.getLogger("lambda_bundler")

@util.return_empty_if_skip_install
def build_layer_package(requirement_files: typing.List[str]) -> str:
    """
    Builds the zip archive for a lambda layer from a list of requirement files.

    :param requirement_files: List of paths to requirement files.
    :type requirement_files: typing.List[str]
    :return: Path to the packaged zip.
    :rtype: str
    """

    collected_dependencies = dependencies.collect_and_merge_requirements(
        *requirement_files
    )

    return dependencies.create_or_return_zipped_dependencies(
        requirements_information=collected_dependencies,
        output_directory_path=util.get_build_dir(),
        prefix_in_zip="python"
    )

@util.return_empty_if_skip_install
def build_lambda_package(code_directories: typing.List[str],
                         requirement_files: typing.List[str] = None,
                         exclude_patterns: typing.List[str] = None) -> str:
    """
    This function builds a lambda deployment package out of one or
    more code directories and optionally bundles dependencies in
    the package.

    :param code_directories: List of paths to the code directories.
    :type code_directories: typing.List[str]
    :param requirement_files: List of paths to requirement files, defaults to None
    :type requirement_files: typing.List[str], optional
    :param exclude_patterns: Glob patterns of files to exclude from the code_directories, defaults to None
    :type exclude_patterns: typing.List[str], optional
    :return: Path to the .zip archive.
    :rtype: str
    """
    if requirement_files is None:

        return dependencies.build_lambda_package_without_dependencies(
            code_directories=code_directories,
            exclude_patterns=exclude_patterns
        )

    return dependencies.build_lambda_package_with_dependencies(
        code_directories=code_directories,
        requirement_files=requirement_files,
        exclude_patterns=exclude_patterns
    )


# given list: arr, Requirement : DRHPaoyoisapsecpyiynth
arr = ["Daisy","Rose","Hyacinth","Poppy"]
#----------------------------------------------------------------------------------------------------------------------------------------------------------------#
#Final Solution
#----------------------------------------------------------------------------------------------------------------------------------------------------------------#

#function which does the job but it requires length of the longest string element and input should be of string type and not a list type
charBuffer = []
def processWords(input,maxLen):
    s = input.split(" ")
    for i in range (0,maxLen):
        for value in s:
            if i>len(value)-1:
                continue
            else:
                charBuffer.append(value[i])
    return charBuffer

#storing length of the longest element of arr list
maxLength = len(max(arr, key=len))
#converting list arr into string inputArr
inputArr = " ".join(map(str,arr))

#passing string inputArr and maxLength to our function and storing it in result
result = processWords(inputArr,maxLength)

#print result
print(*result, sep = "")

#----------------------------------------------------------------------------------------------------------------------------------------------------------------#
#Initial Solution (can be ignored)
#----------------------------------------------------------------------------------------------------------------------------------------------------------------#

#function which does the job but it requires length of the longest string element and input should be of string type and not a list type
charBuffer = []
def processWords(input,maxLen):
    s = input.split(" ")
    #below for loop was failing when the length of all strings were either not equal or generating wrong output : "DRHPaoyoisapsecp"
    #(that's why appended dummy character '*')
    for i in range(0,maxLen):
        for values in s:
            #ignoring dummy symbol '*'
            if values[i] == '*':
                continue
            else:
                charBuffer.append(values[i])
    return charBuffer

#storing length of the longest element of arr list
maxLength = len(max(arr, key=len))
# creating an empty list
myArr=[]
#appending all elements of arr into myArr and concatenating a dummy symbol '*'
#in elements whose length < maxLength before appending it to myArr
for element in arr:
    if len(element) < maxLength:
        count = len(element)
        while count < maxLength:
            element += '*'
            count+=1
        myArr.append(element)
    else:
        myArr.append(element)

#converting list myArr into string inputArr
inputArr = " ".join(map(str,myArr))

#passing string inputArr and maxLength to our function and storing it in result
result = processWords(inputArr,maxLength)

#print result
print(*result, sep = "")


import io
import random
from zipfile import ZipFile

import numpy as np
import torch
from PIL import Image
from torch.utils.data import Dataset
from .transforms_utils import build_transforms
import itertools

try:
    from torchvision.transforms import InterpolationMode

    BICUBIC = InterpolationMode.BICUBIC
except ImportError:
    BICUBIC = Image.BICUBIC

from ..utils import DATASETS

@DATASETS.register_module()
class VideoZipDataSet(torch.utils.data.Dataset):

    def __init__(
            self, vids_path, zip_prefix, width, max_frames, preprocess, original_fps=1
    ):
        self.zip_prefix = zip_prefix
        self.width = width
        self.max_frames = max_frames
        self.original_fps = original_fps
        self.transform = build_transforms(preprocess, width, width)

        with open(vids_path, "r", encoding="utf-8") as f:
            self.vids = [x.strip() for x in f]
        print(f"### Num vids {len(self.vids)}")
        print(f"### Samples {' '.join(self.vids[:5])}")

    def __len__(self):
        return len(self.vids)

    def __getitem__(self, index):
        vid = self.vids[index]
        zip_path = self.zip_prefix % (vid[-2:], vid)
        frames_tensor = torch.zeros(self.max_frames, 3, self.width, self.width)
        frames_mask = torch.zeros(self.max_frames).long()
        timestamps = torch.zeros(self.max_frames, 2).long()

        try:
            with ZipFile(zip_path, 'r') as handler:
                img_name_list = handler.namelist()
                img_name_list = sorted(img_name_list)

                for i, img_name in enumerate(img_name_list):
                    i_img_content = handler.read(img_name)
                    i_img = Image.open(io.BytesIO(i_img_content))
                    i_img_tensor = self.transform(i_img)
                    frames_tensor[i, ...] = i_img_tensor
                    frames_mask[i] = 1
                    timestamps[i] = torch.tensor([i / self.original_fps, (i + 1) / self.original_fps])
        except FileNotFoundError as e:
            print(e)

        record = {
            "name": vid,
            "timestamp": np.array(timestamps),
            "input": frames_tensor,
            "input_mask": frames_mask,
        }

        return record

@DATASETS.register_module()
class PairWiseFeatZipDataSet(torch.utils.data.Dataset):

    def __init__(
            self, query_vids_path, ref_vids_path, ann_path, feat_zip_path, max_frames=256,
            positive_ratio=0.2, num_workers=8
    ):
        self.max_frames = max_frames

        with open(query_vids_path, "r", encoding="utf-8") as f:
            self.q_vids = [x.strip() for x in f]
        print("#### query vid num %d" % len(self.q_vids))
        print("#### query vid samples %s" % " ".join(self.q_vids[:5]))

        with open(ref_vids_path, "r", encoding="utf-8") as f:
            self.r_vids = [x.strip() for x in f]
        print("#### ref vid num %d" % len(self.r_vids))
        print("#### ref vid samples %s" % " ".join(self.r_vids[:5]))

        self.ann = []
        with open(ann_path, "r", encoding="utf-8") as f:
            for line in f:
                q_vid, r_vid = line.strip().split(",")
                self.ann.append((q_vid, r_vid))
        print("#### ann num %d" % len(self.ann))
        self.ann_set = set(self.ann)
        self.pairs = list(itertools.product(self.q_vids, self.r_vids))

        self.handles = []
        for i in range(num_workers):
            self.handles.append(ZipFile(feat_zip_path, 'r'))

        self.positive_ratio = positive_ratio

    def __len__(self):
        return len(self.pairs)
        # return len(self.q_vids)

    def __getitem__(self, index):
        worker_info = torch.utils.data.get_worker_info()

        info = self.sample(index, worker_info.id)

        return info

    def _read_feats(self, vid, worker_id):

        video_feature = np.load(io.BytesIO(self.handles[worker_id].read(vid)), allow_pickle=True).astype(np.float32)
        video_feature = video_feature[:self.max_frames]
        video_feature = torch.tensor(video_feature)

        return video_feature

    def sample(self, index, worker_id):
        labels = 0
        q_vid, r_vid = self.pairs[index]
        if (q_vid, r_vid) in self.ann_set:
            labels = 1
        prob = np.random.random()
        if prob < self.positive_ratio:
            q_vid, r_vid = random.choice(self.ann)
            labels = 1

        # q_vid = self.q_vids[index]
        # if q_vid in self.ann:
        #     prob = np.random.random()
        #     if prob < self.positive_ratio:
        #         sampled_vid = random.choice(self.ann[q_vid])
        #         labels = 1
        #     else:
        #         sampled_vid = random.choice(self.r_vids)
        #         labels = -1
        # else:
        #     sampled_vid = random.choice(self.r_vids)
        #     labels = -1

        feat_a = self._read_feats(q_vid, worker_id)
        feat_b = self._read_feats(r_vid, worker_id)

        res = {
            "frames_a": feat_a, "frames_b": feat_b, "vid_a": q_vid, "vid_b": r_vid, "labels": labels
        }

        return res

@DATASETS.register_module()
class FeatZipDataSet(torch.utils.data.Dataset):

    def __init__(
            self, vids_path, feat_zip_path, max_frames=256, num_workers=8
    ):
        self.max_frames = max_frames

        with open(vids_path, "r", encoding="utf-8") as f:
            self.vids = [x.strip() for x in f]
        print("#### query vid num %d" % len(self.vids))
        print("#### query vid samples %s" % " ".join(self.vids[:5]))

        self.handles = []
        for i in range(num_workers):
            self.handles.append(ZipFile(feat_zip_path, 'r'))

    def __len__(self):
        return len(self.vids)

    def __getitem__(self, index):
        worker_info = torch.utils.data.get_worker_info()

        info = self.sample(index, worker_info.id)

        return info

    def _read_feats(self, vid, worker_id):

        video_feature = np.load(io.BytesIO(self.handles[worker_id].read(vid)), allow_pickle=True).astype(np.float32)
        video_feature = video_feature[:self.max_frames]
        video_feature = torch.tensor(video_feature)

        return video_feature

    def sample(self, index, worker_id):
        vid = self.vids[index]
        video_feature = self._read_feats(vid, worker_id)

        res = {
            "frames": video_feature, "vid": vid
        }

        return res

@DATASETS.register_module()
class LabelFeatZipDataSet(FeatZipDataSet):

    def __init__(
            self, vids_path, feat_zip_path, max_frames=256, num_workers=8, ann_vids_path=""
    ):
        super(LabelFeatZipDataSet, self).__init__(vids_path, feat_zip_path, max_frames, num_workers)

        self.ann_set = set()
        with open(ann_vids_path, "r", encoding="utf-8") as f:
            for line in f:
                self.ann_set.add(line.strip())

    def sample(self, index, worker_id):
        vid = self.vids[index]
        video_feature = self._read_feats(vid, worker_id)
        if vid in self.ann_set:
            labels = 1
        else:
            labels = 0

        res = {
            "frames": video_feature, "vid": vid, "labels": labels
        }

        return res


class TaggedObjectMixinView(object):

    def get_queryset(self):
        queryset = super(TaggedObjectMixinView, self).get_queryset()
        if "tag" in self.kwargs:
            queryset = queryset.filter(tags__name__in=[self.kwargs["tag"]])

        return queryset

class DateObjectMixinView(object):
    def get_queryset(self):
        queryset = super(DateObjectMixinView, self).get_queryset()
        if "year" in self.kwargs and "month" in self.kwargs:
            queryset = queryset.filter(published_on__year=self.kwargs["year"], published_on__month=self.kwargs["month"])

        return queryset

class DraftObjectMixinView(object):
    def get_queryset(self):
        queryset = super(DraftObjectMixinView, self).get_queryset()
        if self.request.user.is_superuser:
            return queryset
        else:
            return queryset.filter(is_published=True)

        return queryset


class Graph:
    def __init__(self,v):
        self.V=v
        self.graph={new_list:[] for new_list in range(v)}

    def addedge(self,u,v):
        self.graph[u].append(v)

    def topologicalSort(self,visited,stack,v):
        visited[v]=True

        for i in self.graph[v]:
            if(visited[i]==False):
                self.topologicalSort(visited, stack, i)

        stack.insert(0, v)
    def TSort(self):
        visited=[False]*self.V

        stack=[]

        for i in range(self.V):
            if(visited[i]==False):
                self.topologicalSort(visited, stack, i)

        print( stack)

e=int(input("Enter no of edgs :: "))
v=int(input("Enter no of vertices :: "))

g=Graph(v)

print("Now, enter edges :: ")

for i in range(e):
    print()
    u=int(input("Enter src :: "))
    v=int(input("Enter dest :: "))
    g.addedge(u,v)

g.TSort()


from typing import Dict, List, Optional, Set
from collections import defaultdict
import logging
import os
import tempfile
import subprocess
import shutil

from allennlp.common.util import is_distributed
from allennlp.common.checks import ConfigurationError
from allennlp.training.metrics.metric import Metric

logger = logging.getLogger(__name__)

DEFAULT_SRL_EVAL_PATH = os.path.abspath(
    os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "tools", "srl-eval.pl")
)

@Metric.register("srl_eval")
class SrlEvalScorer(Metric):
    """
    This class uses the external srl-eval.pl script for computing the CoNLL SRL metrics.

    AllenNLP contains the srl-eval.pl script, but you will need perl 5.x.

    Note that this metric reads and writes from disk quite a bit. In particular, it
    writes and subsequently reads two files per __call__, which is typically invoked
    once per batch. You probably don't want to include it in your training loop;
    instead, you should calculate this on a validation set only.

    # Parameters

    srl_eval_path : `str`, optional.
        The path to the srl-eval.pl script.
    ignore_classes : `List[str]`, optional (default=`None`).
        A list of classes to ignore.
    """

    def __init__(
        self, srl_eval_path: str = DEFAULT_SRL_EVAL_PATH, ignore_classes: List[str] = None
    ) -> None:
        self._srl_eval_path = srl_eval_path
        self._ignore_classes = set(ignore_classes)
        # These will hold per label span counts.
        self._true_positives: Dict[str, int] = defaultdict(int)
        self._false_positives: Dict[str, int] = defaultdict(int)
        self._false_negatives: Dict[str, int] = defaultdict(int)

    def __call__(
        self,  # type: ignore
        batch_verb_indices: List[Optional[int]],
        batch_sentences: List[List[str]],
        batch_conll_formatted_predicted_tags: List[List[str]],
        batch_conll_formatted_gold_tags: List[List[str]],
    ) -> None:

        """
        # Parameters

        batch_verb_indices : `List[Optional[int]]`, required.
            The indices of the verbal predicate in the sentences which
            the gold labels are the arguments for, or None if the sentence
            contains no verbal predicate.
        batch_sentences : `List[List[str]]`, required.
            The word tokens for each instance in the batch.
        batch_conll_formatted_predicted_tags : `List[List[str]]`, required.
            A list of predicted CoNLL-formatted SRL tags (itself a list) to compute score for.
            Use allennlp.models.semantic_role_labeler.convert_bio_tags_to_conll_format
            to convert from BIO to CoNLL format before passing the tags into the metric,
            if applicable.
        batch_conll_formatted_gold_tags : `List[List[str]]`, required.
            A list of gold CoNLL-formatted SRL tags (itself a list) to use as a reference.
            Use allennlp.models.semantic_role_labeler.convert_bio_tags_to_conll_format
            to convert from BIO to CoNLL format before passing the
            tags into the metric, if applicable.
        """
        if not os.path.exists(self._srl_eval_path):
            raise ConfigurationError(f"srl-eval.pl not found at {self._srl_eval_path}.")
        tempdir = tempfile.mkdtemp()
        gold_path = os.path.join(tempdir, "gold.txt")
        predicted_path = os.path.join(tempdir, "predicted.txt")

        with open(predicted_path, "w", encoding="utf-8") as predicted_file, open(
            gold_path, "w", encoding="utf-8"
        ) as gold_file:
            for verb_index, sentence, predicted_tag_sequence, gold_tag_sequence in zip(
                batch_verb_indices,
                batch_sentences,
                batch_conll_formatted_predicted_tags,
                batch_conll_formatted_gold_tags,
            ):
                from allennlp_models.structured_prediction.models.srl import (
                    write_conll_formatted_tags_to_file,
                )

                write_conll_formatted_tags_to_file(
                    predicted_file,
                    gold_file,
                    verb_index,
                    sentence,
                    predicted_tag_sequence,
                    gold_tag_sequence,
                )
        perl_script_command = ["perl", self._srl_eval_path, gold_path, predicted_path]
        try:
            completed_process = subprocess.run(
                perl_script_command, stdout=subprocess.PIPE, universal_newlines=True, check=True
            )
        except FileNotFoundError:
            raise FileNotFoundError(
                "'File not found' while running the evaluation. Do you have perl installed?"
            )
        for line in completed_process.stdout.split("\n"):
            stripped = line.strip().split()
            if len(stripped) == 7:
                tag = stripped[0]
                # Overall metrics are calculated in get_metric, skip them here.
                if tag == "Overall" or tag in self._ignore_classes:
                    continue
                # This line contains results for a span
                num_correct = int(stripped[1])
                num_excess = int(stripped[2])
                num_missed = int(stripped[3])
                self._true_positives[tag] += num_correct
                self._false_positives[tag] += num_excess
                self._false_negatives[tag] += num_missed

        # Note: we cannot aggregate across distributed workers because each worker
        # may end up with different tags, and in such a case, the reduce operation
        # will stall, or return with inaccurate values.
        shutil.rmtree(tempdir)

    def get_metric(self, reset: bool = False):
        """
        # Returns

        A Dict per label containing following the span based metrics:

        - precision : `float`
        - recall : `float`
        - f1-measure : `float`

        Additionally, an `overall` key is included, which provides the precision,
        recall and f1-measure for all spans.
        """
        if is_distributed():
            raise RuntimeError(
                "Distributed aggregation for `SrlEvalScorer` is currently not supported."
            )
        all_tags: Set[str] = set()
        all_tags.update(self._true_positives.keys())
        all_tags.update(self._false_positives.keys())
        all_tags.update(self._false_negatives.keys())
        all_metrics = {}
        for tag in all_tags:
            if tag == "overall":
                raise ValueError(
                    "'overall' is disallowed as a tag type, "
                    "rename the tag type to something else if necessary."
                )
            precision, recall, f1_measure = self._compute_metrics(
                self._true_positives[tag], self._false_positives[tag], self._false_negatives[tag]
            )
            precision_key = "precision" + "-" + tag
            recall_key = "recall" + "-" + tag
            f1_key = "f1-measure" + "-" + tag
            all_metrics[precision_key] = precision
            all_metrics[recall_key] = recall
            all_metrics[f1_key] = f1_measure

        # Compute the precision, recall and f1 for all spans jointly.
        precision, recall, f1_measure = self._compute_metrics(
            sum(self._true_positives.values()),
            sum(self._false_positives.values()),
            sum(self._false_negatives.values()),
        )
        all_metrics["precision-overall"] = precision
        all_metrics["recall-overall"] = recall
        all_metrics["f1-measure-overall"] = f1_measure
        if reset:
            self.reset()
        return all_metrics

    @staticmethod
    def _compute_metrics(true_positives: int, false_positives: int, false_negatives: int):
        precision = true_positives / (true_positives + false_positives + 1e-13)
        recall = true_positives / (true_positives + false_negatives + 1e-13)
        f1_measure = 2.0 * (precision * recall) / (precision + recall + 1e-13)
        return precision, recall, f1_measure

    def reset(self):
        self._true_positives = defaultdict(int)
        self._false_positives = defaultdict(int)
        self._false_negatives = defaultdict(int)


"""
This script recursively searches for MS images files in a given directory, extracts source line information,
and stores the list into a file.
"""
import argparse
import multiprocessing as mp
import os
import re
import subprocess as sb
from concurrent.futures import ThreadPoolExecutor
from dataclasses import dataclass, field
from pathlib import Path
from typing import Optional, List, Set, Callable, Iterable, Tuple

SYMBOLS_FILE_EXT: str = ".binaryrts"

@dataclass
class MicrosoftImageFile:
    """
    Depicts an MS image file.
    """

    image_file: Path
    pdb_file: Optional[Path] = field(default=None)

    @classmethod
    def find_all_image_files_by_ext(
            cls, root_dir: Path, ext: str = "dll", regex: str = ".*"
    ) -> List["MicrosoftImageFile"]:
        files: List[MicrosoftImageFile] = []
        for file in root_dir.rglob(f"*.{ext}"):
            if not re.match(regex, file.name, re.IGNORECASE):
                continue
            pdb_files = list(file.parent.glob("*.pdb"))
            # we only consider image files with .pdb here
            if len(pdb_files) > 0:
                pdb_files.sort(key=lambda f: f.stat().st_size, reverse=True)
                pdb_file: Path = file.parent / pdb_files[0].name
                files.append(cls(image_file=file, pdb_file=pdb_file))
        files.sort(key=lambda f: f.pdb_file.stat().st_size, reverse=True)
        return files

    def __hash__(self) -> int:
        return hash(self.image_file.name)

    def __eq__(self, o: "MicrosoftImageFile") -> bool:
        return o.image_file.name == self.image_file.name

def run_with_multi_processing(func: Callable, arguments: Iterable, n_cpu: int):
    """
    Run a function for each element in arguments with multiprocessing.
    """
    with ThreadPoolExecutor(max_workers=n_cpu) as executor:
        for args in arguments:
            executor.submit(func, *args)

def call_extractor(executable: Path, input_file: Path, sources_regex: str):
    command: str = " ".join(
        [
            f'"{executable.__str__()}"',  # quotes to support whitespaces in path
            f'-input "{input_file.__str__()}"',
            f'-regex "{sources_regex}"',
            # FIXME: maybe this could be changed to '-mode symbols' in the future,
            #  but debug symbols are unreliable at the moment
            f"-mode lines",
        ]
    )
    process: sb.CompletedProcess = sb.run(
        command,
        text=True,
        capture_output=True,
    )
    print(
        f"Exit code: {process.returncode}\nstdout: {process.stdout}\nstderr: {process.stderr}"
    )

def parse_arguments() -> argparse.Namespace:
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "--input-path",
        "-i",
        default=os.getcwd(),
        help=f"Root directory from where to search for image files.",
    )
    parser.add_argument(
        "--extractor",
        required=True,
        help="Path to binary_rts_extractor executable.",
    )
    parser.add_argument(
        "--ext",
        default="dll",
        choices=["dll", "exe"],
        help="Image file extension.",
    )
    parser.add_argument(
        "--cpus",
        default=mp.cpu_count(),
        type=int,
        help="Process count for parallelization.",
    )
    parser.add_argument(
        "--image-filter", default=".*", help="Pattern to filter image names."
    )
    parser.add_argument(
        "--sources",
        default=".*mb2cpp(?!.external).*",
        help="Pattern to filter for source files.",
    )
    return parser.parse_args()

def main():
    # Parse arguments
    args = parse_arguments()
    root_dir: Path = Path(args.input_path).absolute()
    extractor: Path = Path(args.extractor).absolute()
    image_files: Set[MicrosoftImageFile] = set(
        MicrosoftImageFile.find_all_image_files_by_ext(root_dir=root_dir, ext=args.ext, regex=args.image_filter)
    )
    mp_args: List[Tuple[Path, Path, str]] = [
        (extractor, file.image_file, args.sources) for file in image_files
    ]
    run_with_multi_processing(
        func=call_extractor,
        arguments=mp_args,
        n_cpu=args.cpus,
    )

if __name__ == "__main__":
    main()


# Anki Database Helper Functions
# Special Thanks to Tim Wilson for this code!

import datetime
import time
from aqt import mw
from anki.utils import ids2str, intTime
from .ah_common import AnkiHabiticaCommon as ah

timebox_seconds = 900  # hard-set 'timebox' length in seconds
# having a hard-coded 'timebox' length solves the problem of
# a user changing a timebox setting and suddenly having a ton
# of new points.

# Return current local time as seconds from epoch
# DEPRECATED: We'll use Anki own function for this (intTime)
#    which is just time.time() multiplied be a scaling factor
#    of 1000 for reviews and defaults to 1 for everything else
# def newTime():
#    return int((datetime.datetime.now() - datetime.datetime.fromtimestamp(0)).total_seconds())

# Return a formatted date string
def prettyTime(secFromEpoch):
    if ah.user_settings["keep_log"]:
        ah.log.debug("Begin function")
    out = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(secFromEpoch))
    if ah.user_settings["keep_log"]:
        ah.log.debug("End function returning: %s" % out)
    return out

# Find correct answers since start_date
def correct_answer_count(start_date):
    if ah.user_settings["keep_log"]:
        ah.log.debug("Begin function")
    # only get number of correct answers
    dbScore = mw.col.db.scalar("""select
        count()
        from revlog where
        ease is not 1 and
        id/1000 > ?
        """, start_date)
    if ah.user_settings["keep_log"]:
        ah.log.debug("End function returning: %s" % dbScore)
    return dbScore

# Find wrong answers since start_date
def wrong_answer_count(start_date):
    if ah.user_settings["keep_log"]:
        ah.log.debug("Begin function")
    # only get number of wrong answers
    dbScore = mw.col.db.scalar("""
        select
        count()
        from revlog where
        ease = 1 and
        id/1000 > ?
        """, start_date)
    if ah.user_settings["keep_log"]:
        ah.log.debug("End function returning: %s" % dbScore)
    return dbScore

# Find total time in seconds of all activity since start_date
def seconds_count(start_date):
    if ah.user_settings["keep_log"]:
        ah.log.debug("Begin function")
    dbTime = mw.col.db.scalar("""
        select
        sum(time)/1000
        from revlog where
        id/1000 > ?
        """, start_date)
    # In case no reviews were found, dbTime will be None, so fix that
    if dbTime is None:
        dbTime = 0
    if ah.user_settings["keep_log"]:
        ah.log.debug("End function returning: %s" % dbTime)
    return dbTime

# Find timeboxes since start_date
def timebox_count(start_date):
    if ah.user_settings["keep_log"]:
        ah.log.debug("Begin function")
    db_time = seconds_count(start_date)
    # Original code used actual timebox settings
    # but this leads to big problems when the user
    # changes the timebox length.
    # if mw.col.conf['timeLim'] > 0:
    #    db_count = dbTime // mw.col.conf['timeLim']
    # else:
    #    db_count = 0
    db_count = db_time // timebox_seconds
    if ah.user_settings["keep_log"]:
        ah.log.debug("End function returning: %s" % db_count)
    return db_count

# Count mature cards
def matured_count(start_date):
    if ah.user_settings["keep_log"]:
        ah.log.debug("Begin function")
    dbMatured = mw.col.db.scalar("""
        select count()
        from revlog
        where ivl >= 21 and
        lastIvl < 21
        and id/1000 > ?
        """, start_date)
    if dbMatured is None:
        dbMatured = 0
    if ah.user_settings["keep_log"]:
        ah.log.debug("End function returning: %s" % dbMatured)
    return dbMatured

# Count new learing cards - cards entering the learning queue (type = 0)
#    for the first time.
# Note: Learning cards may have several reviews and all show up as type=0
#      so we do a subquery to select the minimum review id and group by card id
def learned_count(start_date):
    if ah.user_settings["keep_log"]:
        ah.log.debug("Begin function")
    learned = 0
    learned = mw.col.db.scalar("""
        select count() from
        (select min(id) as id
        from revlog
        where type = 0
        group by cid) as s
        where id/1000 > ?
        """, start_date)
    if learned is None:
        learned = 0
    if ah.user_settings["keep_log"]:
        ah.log.debug("End function returning: %s" % learned)
    return learned

# Find number of decks completed
def decks_count(start_date):
    if ah.user_settings["keep_log"]:
        ah.log.debug("Begin function")
    '''
    new decks from db
    Here it takes a little bit of work.
    For each deck we'll get the number of reviews due for each
    day since start_date and the number of reviews performed for
    the same days.
    For a given day, if there are zero reviews due but more than
    zero reviews completed, then it means the user finished all reviews
    for that day.
    '''
    dbDecks = 0
    finishedDecks = []
    # See how many previous days we need to check
    numDays = int((intTime() - start_date)/86400)+2
    for d in mw.col.decks.all():
        # get list of number of cards due for deck each day
        cardsDue = [(-nDays, mw.col.db.scalar("""
            select
            count()
            from cards where
            queue in (2,3) and
            did = ? and
            due <= ?
            """, (d['id'], mw.col.sched.today-nDays))) for nDays in range(numDays)]
        # get list of number of cards reviewed for deck each day
        cardsDone = mw.col.db.all("""
            select
            (cast((id/1000.0 - ?) / 86400.0 as int)) as day,
            sum(case when cid in %s then 1 else 0 end)
            from revlog where
            id/1000 > ?
            group by day order by day
            """ % ids2str([x[0] for x in mw.col.db.all("""
                select
                id
                from cards where
                did = ?
                """, d['id'])]),
            (mw.col.sched.dayCutoff, start_date))
        '''
        The db.all() call above only returns data for days that have >= 1 completed review,
        so there is not in general a one-to-one corresponce between cardsDue and cardsDone
        (cardsDue[i] and cardsDone[i] might represent different days).
        So need to use the below ugly code to make sure that cardsDue[i] and cardsDone[j]
        are for the same day.
        '''
        if not cardsDue is None and not cardsDone is None:
            for due in cardsDue:
                if due[1] == 0:
                    for done in cardsDone:
                        if done[0] == due[0]:
                            if done[1] > 0 and due[1] == 0:
                                dbDecks += 1
                                finishedDecks.append(d)
                            break
    '''
    We've got the full list of completed decks, but there could be extras (i.e. if a user
    completes two children decks by completing the parent deck and the parent deck doesn't
    contain any cards, then it will count as 3 decks when it should count as 2.
    So check that a parent deck actually has cards of its own before giving credit for it.
    Check by getting combined cards of all child decks. If parent deck card count is not
    more than the combined child deck card count then the parent deck doesn't have any of
    its own cards and shouldn't be counted.
    '''
    for p in finishedDecks:
        children = mw.col.decks.children(p['id'])
        cCardCount = 0
        for c in children:
            cCardCount += mw.col.db.scalar("""
                select count() from cards where did = ?
                """, c[1])
        if cCardCount > 0:
            pCardCount = mw.col.db.scalar("""
                select count() from cards where did = ?
                """, p['id'])
            if not pCardCount > cCardCount:
                dbDecks -= 1

    if ah.user_settings["keep_log"]:
        ah.log.debug("End function returning: %s" % dbDecks)
    return dbDecks

# return the time (seconds since epoch) of the most recent review in the database
def latest_review_time():
    if ah.user_settings["keep_log"]:
        ah.log.debug("Begin function")
    out = mw.col.db.scalar("select max(id/1000) from revlog")
    if out is None:
        out = intTime()
    if ah.user_settings["keep_log"]:
        ah.log.debug("End function returning: %s (%s)" %
                     (out, prettyTime(out)))
    return out


from turtle import *  # импортирую черепашку
from random import randint, choice

t = Turtle()  # главный объект (она рисует)
t.shape('turtle')  # устанавливаю форму черепашки

colors = ['yellow', 'orange', 'purple', 'DeepSkyBlue',
          'coral', 'gold', 'hotpink', 'SlateBlue1']
t.up()

for i in range(100):
    t.color(choice(colors))  # беру случайный цвет линии
    t.fillcolor(choice(colors))  # беру случайный цвет заливки
    x = randint(-500, 500)
    y = randint(-500, 500)
    t.goto(x, y)
    t.dot(randint(5, 100))

done()


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Tue May  5 18:57:25 2020

@author: jinlingxing
"""

'''
Given an integer, write an algorithm to convert it to hexadecimal. For negative integer, two’s complement method is used.

Note:

All letters in hexadecimal (a-f) must be in lowercase.
The hexadecimal string must not contain extra leading 0s. If the number is zero, it is represented by a single zero character '0'; otherwise, the first character in the hexadecimal string will not be the zero character.
The given number is guaranteed to fit within the range of a 32-bit signed integer.
You must not use any method provided by the library which converts/formats the number to hex directly.
Example 1:

Input:
26

Output:
"1a"
Example 2:

Input:
-1

Output:
"ffffffff"
'''

class Solution:
    def toHex(self, num: int) -> str:

        l=[]
        dic = {10:'a',11:'b',12:'c',13:'d',14:'e',15:'f'}
        if num == 0:
            return '0'
        if num<0:
            num = num+2**32
        while(num!=0):
            reminder = num%16
            num = num//16
            if reminder >=10:
                l = [dic[reminder]]+l
            else:
                l = [str(reminder)]+l
        return "".join(l)

math/405. convert a number to hexadecimal.py


from .dense import Dense
class MLP:
    def __init__(self, nin, uout, uhidden):
        self.num_layer = len(uhidden)+1
        prev = nin
        self.layers = []
        for u in uhidden:
            self.layers.append(Dense(prev, u))
            prev = u
        self.layers.append(Dense(prev, uout))

    def __call__(self, x, relu=False):

        x1 = self.layers[0](x, relu=True)
        for iterate in range(1, self.num_layer-1):
            variable1 = "x"+str(iterate+1)
            variable2 = "x"+str(iterate)
            vars()[variable1] = self.layers[iterate](vars()[variable2], relu=True)

        variable_name = "x" + str(self.num_layer-1)
        y = self.layers[self.num_layer-1](vars()[variable_name], relu = True)

        exec_string = ""
        for j in range(1, self.num_layer):
            exec_string = exec_string + "x" + str(j) + ", "
        exec_string = exec_string + "y"

        return list(eval(exec_string))


import collections
class Solution:
    def sumOfDistancesInTree(self, N, edges):
        """
        :type N: int
        :type edges: List[List[int]]
        :rtype: List[int]
        """
        graph = collections.defaultdict(set)
        for u, v in edges:
            graph[u].add(v)
            graph[v].add(u)

        count = [1] * N
        res = [0] * N
        def dfs1(node = 0, parent = None):
            for child in graph[node]:
                if child != parent:
                    dfs1(child, node)
                    count[node] += count[child]
                    res[node] += res[child] + count[child]

        def dfs2(node = 0, parent = None):
            for child in graph[node]:
                if child != parent:
                    res[child] = res[node] - count[child] + N - count[child]
                    dfs2(child, node)

        dfs1()
        dfs2()
        return res


from itertools import accumulate
from bisect import *

n, p, q, r = map(int, input().split())
a = list(map(int, input().split()))

acc = [0] + list(accumulate(a))
for pos_x in range(len(acc)):
    val_x = acc[pos_x]

    pos_y = bisect_left(acc, val_x + p, lo=pos_x)
    if pos_y == len(acc):
        continue
    val_y = acc[pos_y]

    pos_z = bisect_left(acc, val_y + q, lo=pos_y)
    if pos_z == len(acc):
        continue
    val_z = acc[pos_z]

    pos_w = bisect_left(acc, val_z + r, lo=pos_z)
    if pos_w == len(acc):
        continue
    val_w = acc[pos_w]

    if val_y - val_x == p and val_z - val_y == q and val_w - val_z == r:
        print("Yes")
        exit()
print("No")


import gym_super_mario_bros
from nes_py.wrappers import JoypadSpace
from gym_super_mario_bros.actions import SIMPLE_MOVEMENT
from gym.wrappers import GrayScaleObservation
from stable_baselines3.common.vec_env import VecFrameStack, DummyVecEnv
from matplotlib import pyplot as plt
import os
from stable_baselines3 import PPO
from stable_baselines3.common.callbacks import BaseCallback

CHECKPOINT_DIR = './train/'
LOG_DIR = './logs/'

MODEL_NUM = 1

#PREPROCESS ENV
#setup env
env = gym_super_mario_bros.make('SuperMarioBros-v0')
env = JoypadSpace(env, SIMPLE_MOVEMENT)

#wrap in grayscale
env = GrayScaleObservation(env, keep_dim=True)

#wrap in dummy env
env = DummyVecEnv([lambda: env])

#stack frames
env = VecFrameStack(env, 4, channels_order='last')

#SAVE MODEL
class TrainAndLoggingCallback(BaseCallback):

    def __init__(self, check_freq, save_path, verbose=1):
        super(TrainAndLoggingCallback, self).__init__(verbose)
        self.check_freq = check_freq
        self.save_path = save_path

    def _init_callback(self):
        if self.save_path is not None:
            os.makedirs(self.save_path, exist_ok=True)

    def _on_step(self):
        if self.n_calls % self.check_freq == 0:
            model_path = os.path.join(self.save_path, 'best_model_{}'.format(MODEL_NUM))
            self.model.save(model_path)

        return True

callback = TrainAndLoggingCallback(check_freq=5000, save_path=CHECKPOINT_DIR)

#IMPLEMENT RL MODEL

#new model
#model = PPO('CnnPolicy', env, verbose=1, tensorboard_log=LOG_DIR, learning_rate=0.0000005, n_steps=512)

#load model
model = PPO.load('./train/best_model_1.zip')

#train model
#model.learn(total_timesteps=10000000, callback=callback)

#run model
state = env.reset()
while True:
    action, _state = model.predict(state)
    state, reward, done, info = env.step(action)
    env.render()