import os
import h5py
import numpy as np
from copy import deepcopy
from scipy.stats import chi2
from openquake.hazardlib.gsim.base import CoeffsTable
from openquake.hazardlib.gsim.gmpe_table import GMPETable, hdf_arrays_to_dict
from openquake.hazardlib.imt import PGV
from openquake.hazardlib import const

# Common interpolation function
def ITPL(mag, tu, tl, ml, f):
    return tl + (tu - tl) * ((mag - ml) / f)

def _scaling(mean_tau, sd_tau2):
    """
    Returns the chi-2 scaling factor from the mean and variance of the
    uncertainty model, as reported in equation 5.4 of Al Atik (2015)
    """
    return (sd_tau2 ** 2.) / (2.0 * mean_tau ** 2.)

def _dof(mean_tau, sd_tau2):
    """
    Returns the degrees of freedom for the chi-2 distribution from the mean and
    variance of the uncertainty model, as reported in equation 5.5 of Al Atik
    (2015)
    """
    return (2.0 * mean_tau ** 4.) / (sd_tau2 ** 2.)

def _at_percentile(tau, var_tau, percentile):
    """
    Returns the value of the inverse chi-2 distribution at the given
    percentile from the mean and variance of the uncertainty model, as
    reported in equations 5.1 - 5.3 of Al Atik (2015)
    """
    assert (percentile >= 0.0) and (percentile <= 1.0)
    c_val = _scaling(tau, var_tau)
    k_val = _dof(tau, var_tau)
    return np.sqrt(c_val * chi2.ppf(percentile, df=k_val))

# Mean tau values from the global model - Table 5.1
GLOBAL_TAU_MEAN = {
    "PGV": {"tau1": 0.3733, "tau2": 0.3639, "tau3": 0.3434, "tau4": 0.3236},
    "SA": {"tau1": 0.4518, "tau2": 0.4270, "tau3": 0.3863, "tau4": 0.3508}
    }

# Standard deviation of tau values from the global model - Table 5.1
GLOBAL_TAU_SD = {
    "PGV": {"tau1": 0.0558, "tau2": 0.0554, "tau3": 0.0477, "tau4": 0.0449},
    "SA": {"tau1": 0.0671, "tau2": 0.0688, "tau3": 0.0661, "tau4": 0.0491}
}

# Mean tau values from the CENA constant-tau model
CENA_CONSTANT_TAU_MEAN = {"PGV": {"tau": 0.3441}, "SA": {"tau": 0.3695}}

# Standard deviation of tau values from CENA constant-tau model
CENA_CONSTANT_TAU_SD = {"PGV": {"tau": 0.0554}, "SA": {"tau": 0.0688}}

# Mean tau values from the CENA tau model
CENA_TAU_MEAN = {
    "PGV": {"tau1": 0.3477, "tau2": 0.3281, "tau3": 0.3092},
    "SA": {"tau1": 0.3730, "tau2": 0.3375, "tau3": 0.3064}
    }

# Standard deviation of tau values from the CENA tau model
CENA_TAU_SD = {
    "PGV": {"tau1": 0.0554, "tau2": 0.0477, "tau3": 0.0449},
    "SA": {"tau1": 0.0688, "tau2": 0.0661, "tau3": 0.0491}
    }

def global_tau(imt, mag, params):
    """
    'Global' model of inter-event variability, as presented in equation 5.6
    (p103)
    """
    if isinstance(imt, PGV):
        C = params["PGV"]
    else:
        C = params["SA"]
    if mag > 6.5:
        return C["tau4"]
    elif (mag > 5.5) and (mag <= 6.5):
        return ITPL(mag, C["tau4"], C["tau3"], 5.5, 1.0)
    elif (mag > 5.0) and (mag <= 5.5):
        return ITPL(mag, C["tau3"], C["tau2"], 5.0, 0.5)
    elif (mag > 4.5) and (mag <= 5.0):
        return ITPL(mag, C["tau2"], C["tau1"], 4.5, 0.5)
    else:
        return C["tau1"]

def cena_constant_tau(imt, mag, params):
    """
    Returns the inter-event tau for the constant tau case
    """
    if isinstance(imt, PGV):
        return params["PGV"]["tau"]
    else:
        return params["SA"]["tau"]

def cena_tau(imt, mag, params):
    """
    Returns the inter-event standard deviation, tau, for the CENA case
    """
    if isinstance(imt, PGV):
        C = params["PGV"]
    else:
        C = params["SA"]
    if mag > 6.5:
        return C["tau3"]
    elif (mag > 5.5) and (mag <= 6.5):
        return ITPL(mag, C["tau3"], C["tau2"], 5.5, 1.0)
    elif (mag > 5.0) and (mag <= 5.5):
        return ITPL(mag, C["tau2"], C["tau1"], 5.0, 0.5)
    else:
        return C["tau1"]

def get_tau_at_quantile(mean, stddev, quantile):
    """
    Returns the value of tau at a given quantile in the form of a dictionary
    organised by intensity measure
    """
    tau_model = {}
    for imt in mean:
        tau_model[imt] = {}
        for key in mean[imt]:
            if quantile is None:
                tau_model[imt][key] = mean[imt][key]
            else:
                tau_model[imt][key] = _at_percentile(mean[imt][key],
                                                     stddev[imt][key],
                                                     quantile)
    return tau_model

# Gather tau values into dictionary
TAU_SETUP = {
    "cena": {"MEAN": CENA_TAU_MEAN, "STD": CENA_TAU_SD},
    "cena_constant": {"MEAN": CENA_CONSTANT_TAU_MEAN,
                      "STD": CENA_CONSTANT_TAU_SD},
    "global": {"MEAN": GLOBAL_TAU_MEAN, "STD": GLOBAL_TAU_SD}
    }

# Gather tau model implementation functions into dictionary
TAU_EXECUTION = {
    "cena": cena_tau,
    "cena_constant": cena_constant_tau,
    "global": global_tau}

# Phi_ss coefficients for the global model
PHI_SS_GLOBAL = CoeffsTable(sa_damping=5., table="""\
imt     mean_a   var_a  mean_b  var_b
pgv     0.5034  0.0609  0.3585  0.0316
pga     0.5477  0.0731  0.3505  0.0412
0.010   0.5477  0.0731  0.3505  0.0412
0.020   0.5464  0.0727  0.3505  0.0416
0.030   0.5450  0.0723  0.3505  0.0419
0.040   0.5436  0.0720  0.3505  0.0422
0.050   0.5424  0.0716  0.3505  0.0425
0.075   0.5392  0.0707  0.3505  0.0432
0.100   0.5361  0.0699  0.3505  0.0439
0.150   0.5299  0.0682  0.3543  0.0453
0.200   0.5240  0.0666  0.3659  0.0465
0.250   0.5183  0.0651  0.3765  0.0476
0.300   0.5127  0.0637  0.3876  0.0486
0.400   0.5022  0.0611  0.4066  0.0503
0.500   0.4923  0.0586  0.4170  0.0515
0.750   0.4704  0.0535  0.4277  0.0526
1.000   0.4519  0.0495  0.4257  0.0508
1.500   0.4231  0.0439  0.4142  0.0433
2.000   0.4026  0.0405  0.4026  0.0396
3.000   0.3775  0.0371  0.3775  0.0366
4.000   0.3648  0.0358  0.3648  0.0358
5.000   0.3583  0.0353  0.3583  0.0356
7.500   0.3529  0.0350  0.3529  0.0355
10.00   0.3519  0.0350  0.3519  0.0355
""")

# Phi_ss coefficients for the CENA model
PHI_SS_CENA = CoeffsTable(sa_damping=5., table="""\
imt     mean_a   var_a  mean_b   var_b
pgv     0.5636  0.0807  0.4013  0.0468
pga     0.5192  0.0693  0.3323  0.0364
0.010   0.5192  0.0693  0.3323  0.0364
0.020   0.5192  0.0693  0.3331  0.0365
0.030   0.5192  0.0693  0.3339  0.0365
0.040   0.5192  0.0693  0.3348  0.0367
0.050   0.5192  0.0693  0.3355  0.0367
0.075   0.5192  0.0693  0.3375  0.0370
0.100   0.5192  0.0693  0.3395  0.0372
0.150   0.5192  0.0693  0.3471  0.0382
0.200   0.5192  0.0693  0.3625  0.0402
0.250   0.5192  0.0693  0.3772  0.0423
0.300   0.5192  0.0693  0.3925  0.0446
0.400   0.5192  0.0693  0.4204  0.0492
0.500   0.5192  0.0693  0.4398  0.0527
0.750   0.5192  0.0693  0.4721  0.0590
1.000   0.5192  0.0693  0.4892  0.0626
1.500   0.5192  0.0693  0.5082  0.0668
2.000   0.5192  0.0693  0.5192  0.0693
3.000   0.5192  0.0693  0.5192  0.0693
4.000   0.5192  0.0693  0.5192  0.0693
5.000   0.5192  0.0693  0.5192  0.0693
7.500   0.5192  0.0693  0.5192  0.0693
10.00   0.5192  0.0693  0.5192  0.0693
""")

# Phi_ss coefficients for the CENA constant-phi model
PHI_SS_CENA_CONSTANT = CoeffsTable(sa_damping=5., table="""\
imt     mean_a    var_a   mean_b   var_b
pgv     0.5507   0.0678   0.5507  0.0678
pga     0.5132   0.0675   0.5132  0.0675
0.010   0.5132   0.0675   0.5132  0.0675
10.00   0.5132   0.0675   0.5132  0.0675
""")

# Phi_s2ss coefficients for the CENA
PHI_S2SS_CENA = CoeffsTable(sa_damping=5., table="""\
imt       mean      var
pgv     0.4344   0.0200
pga     0.4608   0.0238
0.010   0.4608   0.0238
0.020   0.4617   0.0238
0.030   0.4700   0.0240
0.040   0.4871   0.0260
0.050   0.5250   0.0290
0.075   0.5800   0.0335
0.100   0.5930   0.0350
0.150   0.5714   0.0325
0.200   0.5368   0.0296
0.250   0.5058   0.0272
0.300   0.4805   0.0250
0.400   0.4440   0.0212
0.500   0.4197   0.0182
0.750   0.3849   0.0139
1.000   0.3667   0.0135
1.500   0.3481   0.0157
2.000   0.3387   0.0173
3.000   0.3292   0.0195
4.000   0.3245   0.0211
5.000   0.3216   0.0224
7.500   0.3178   0.0240
10.00   0.3159   0.0240
""")

def get_phi_ss_at_quantile(phi_model, quantile):
    """
    Returns the phi_ss values at the specified quantile as an instance of
    `class`:: openquake.hazardlib.gsim.base.CoeffsTable - applies to the
    magnitude-dependent cases
    """
    # Setup SA coeffs - the backward compatible Python 2.7 way
    coeffs = deepcopy(phi_model.sa_coeffs)
    coeffs.update(phi_model.non_sa_coeffs)
    for imt in coeffs:
        if quantile is None:
            coeffs[imt] = {"a": phi_model[imt]["mean_a"],
                           "b": phi_model[imt]["mean_b"]}
        else:
            coeffs[imt] = {
                "a": _at_percentile(phi_model[imt]["mean_a"],
                                    phi_model[imt]["var_a"],
                                    quantile),
                "b": _at_percentile(phi_model[imt]["mean_b"],
                                    phi_model[imt]["var_b"],
                                    quantile)
                }
    return CoeffsTable(sa_damping=5., table=coeffs)

def get_phi_s2ss_at_quantile(phi_model, quantile):
    """
    Returns the phi_s2ss value for all periods at the specific quantile as
    an instance of `class`::openquake.hazardlib.gsim.base.CoeffsTable
    """
    # Setup SA coeffs - the backward compatible Python 2.7 way
    coeffs = deepcopy(phi_model.sa_coeffs)
    coeffs.update(phi_model.non_sa_coeffs)
    for imt in coeffs:
        if quantile is None:
            coeffs[imt] = {"phi_s2ss": phi_model[imt]["mean"]}
        else:
            coeffs[imt] = {"phi_s2ss": _at_percentile(phi_model[imt]["mean"],
                                                      phi_model[imt]["var"],
                                                      quantile)}
    return CoeffsTable(sa_damping=5., table=coeffs)

# Gather the models to setup the phi_ss values for the given quantile
PHI_SETUP = {
    "cena": PHI_SS_CENA,
    "cena_constant": PHI_SS_CENA_CONSTANT,
    "global": PHI_SS_GLOBAL}

# Phi site-to-site model for th Central & Eastern US
PHI_S2SS_MODEL = {"cena": PHI_S2SS_CENA}

def get_phi_ss(imt, mag, params):
    """
    Returns the single station phi (or it's variance) for a given magnitude
    and intensity measure type according to equation 5.14 of Al Atik (2015)
    """
    C = params[imt]
    if mag <= 5.0:
        phi = C["a"]
    elif mag > 6.5:
        phi = C["b"]
    else:
        phi = C["a"] + (mag - 5.0) * ((C["b"] - C["a"]) / 1.5)
    return phi

class NGAEastBaseGMPE(GMPETable):
    """
    A generalised base class for the implementation of a GMPE in which the
    mean values are determined from tables (input by the user) and the standard
    deviation model taken from Al Atik (2015). Should be common to all
    NGA East ground motion models.

    :param str tau_model:
        Choice of model for the inter-event standard deviation (tau), selecting
        from "global" {default}, "cena" or "cena_constant"

    :param str phi_model:
        Choice of model for the single-station intra-event standard deviation
        (phi_ss), selecting from "global" {default}, "cena" or "cena_constant"

    :param str phi_s2ss_model:
        Choice of station-term s2ss model. Can be either "cena" or None. When
        None is input then the non-ergodic model is used

    :param TAU:
        Inter-event standard deviation model

    :param PHI_SS:
        Single-station standard deviation model

    :param PHI_S2SS:
        Station term for ergodic standard deviation model

    :param bool ergodic:
        True if an ergodic model is selected, False otherwise
    """
    DEFINED_FOR_STANDARD_DEVIATION_TYPES = set((const.StdDev.TOTAL,
                                                const.StdDev.INTER_EVENT,
                                                const.StdDev.INTRA_EVENT))

    def __init__(self, gmpe_table, tau_model="global", phi_model="global",
                 phi_s2ss_model=None, tau_quantile=None,
                 phi_ss_quantile=None, phi_s2ss_quantile=None):
        """
        Instantiates the class with additional terms controlling which
        type of aleatory uncertainty model is preferred ('global',
        'cena_constant', 'cena'), and the quantile of the epistemic uncertainty
        model (float in the range 0 to 1).

        :param float tau_quantile:
            Epistemic uncertainty quantile for the inter-event standard
            deviation models. Float in the range 0 to 1, or None (mean value
            used)

        :param float phi_ss_quantile:
            Epistemic uncertainty quantile for the intra-event standard
            deviation models. Float in the range 0 to 1, or None (mean value
            used)

        :param float phi_s2ss_quantile:
            Epistemic uncertainty quantile for the site-to-site standard
            deviation models. Float in the range 0 to 1, or None (mean value
            used)
        """
        super().__init__(gmpe_table=gmpe_table)
        self.tau_model = tau_model
        self.phi_model = phi_model
        self.phi_s2ss_model = phi_s2ss_model
        self.TAU = None
        self.PHI_SS = None
        self.PHI_S2SS = None
        if self.phi_s2ss_model:
            self.ergodic = True
        else:
            self.ergodic = False
        self._setup_standard_deviations(
            tau_quantile, phi_ss_quantile, phi_s2ss_quantile)
        if os.path.exists(self.GMPE_TABLE):
            with h5py.File(self.GMPE_TABLE, "r") as f:
                self.init(f)

    def init(self, fle):
        """
        Executes the preprocessing steps at the instantiation stage to read in
        the tables from hdf5 and hold them in memory.
        """
        self.distance_type = fle["Distances"].attrs["metric"]
        self.REQUIRES_DISTANCES = set([self.distance_type])
        # Load in magnitude
        self.m_w = fle["Mw"][:]
        # Load in distances
        self.distances = fle["Distances"][:]
        # Load intensity measure types and levels
        self.imls = hdf_arrays_to_dict(fle["IMLs"])
        self.DEFINED_FOR_INTENSITY_MEASURE_TYPES = set(self._supported_imts())
        if "SA" in self.imls.keys() and "T" not in self.imls:
            raise ValueError("Spectral Acceleration must be accompanied by "
                             "periods")
        # Get the standard deviations
        if "Amplification" in fle:
            self._setup_amplification(fle)

    def _setup_standard_deviations(self, tau_quantile, phi_ss_quantile,
                                   phi_s2ss_quantile):
        """
        Reads the standard deviation tables from hdf5 and stores them in
        memory
        :param fle:
            HDF5 Tables as instance of :class:`h5py.File`
        """
        # setup tau
        self.TAU = get_tau_at_quantile(TAU_SETUP[self.tau_model]["MEAN"],
                                       TAU_SETUP[self.tau_model]["STD"],
                                       tau_quantile)
        # setup phi
        self.PHI_SS = get_phi_ss_at_quantile(PHI_SETUP[self.phi_model],
                                             phi_ss_quantile)
        # if required setup phis2ss
        if self.ergodic:
            self.PHI_S2SS = get_phi_s2ss_at_quantile(
                PHI_S2SS_MODEL[self.phi_s2ss_model],
                phi_s2ss_quantile)

    def get_mean_and_stddevs(self, sctx, rctx, dctx, imt, stddev_types):
        """
        Returns the mean and standard deviations
        """
        # Return Distance Tables
        imls = self._return_tables(rctx.mag, imt, "IMLs")
        # Get distance vector for the given magnitude
        idx = np.searchsorted(self.m_w, rctx.mag)
        dists = self.distances[:, 0, idx - 1]
        # Get mean and standard deviations
        mean = self._get_mean(imls, dctx, dists)
        nsites = getattr(dctx, self.distance_type).shape
        stddevs = self.get_stddevs(rctx.mag, imt, stddev_types, nsites)
        if self.amplification:
            # Apply amplification
            mean_amp, sigma_amp = self.amplification.get_amplification_factors(
                imt,
                sctx,
                rctx,
                getattr(dctx, self.distance_type),
                stddev_types)
            mean = np.log(mean) + np.log(mean_amp)
            for iloc in range(len(stddev_types)):
                stddevs[iloc] *= sigma_amp[iloc]
            return mean, stddevs
        else:
            return np.log(mean), stddevs

    def get_stddevs(self, mag, imt, stddev_types, num_sites):
        """
        Returns the standard deviations for either the ergodic or
        non-ergodic models
        """
        tau = self._get_tau(imt, mag)
        phi = self._get_phi(imt, mag)
        sigma = np.sqrt(tau ** 2. + phi ** 2.)
        stddevs = []
        for stddev_type in stddev_types:
            assert stddev_type in self.DEFINED_FOR_STANDARD_DEVIATION_TYPES
            if stddev_type == const.StdDev.TOTAL:
                stddevs.append(sigma + np.zeros(num_sites))
            elif stddev_type == const.StdDev.INTRA_EVENT:
                stddevs.append(phi + np.zeros(num_sites))
            elif stddev_type == const.StdDev.INTER_EVENT:
                stddevs.append(tau + np.zeros(num_sites))
        return stddevs

    def _get_tau(self, imt, mag):
        """
        Returns the inter-event standard deviation (tau)
        """
        return TAU_EXECUTION[self.tau_model](imt, mag, self.TAU)

    def _get_phi(self, imt, mag):
        """
        Returns the within-event standard deviation (phi)
        """
        phi = get_phi_ss(imt, mag, self.PHI_SS)
        if self.ergodic:
            C = self.PHI_S2SS[imt]
            phi = np.sqrt(phi ** 2. + C["phi_s2ss"] ** 2.)
        return phi

# For the total standard deviation model the magnitude boundaries depend on
# the model selected
MAG_LIMS_KEYS = {
    "cena": {"mag": [5.0, 5.5, 6.5], "keys": ["tau1", "tau2", "tau3"]},
    "cena_constant": {"mag": [np.inf], "keys": ["tau"]},
    "global": {"mag": [4.5, 5.0, 5.5, 6.5],
               "keys": ["tau1", "tau2", "tau3", "tau4"]}
    }

class NGAEastGMPE(NGAEastBaseGMPE):
    """
    For the "core" NGA East set the table is provided in the code in a
    subdirectory fixed to the path of the present file. The GMPE table option
    is therefore no longer needed
    """
    NGA_EAST_TABLE = ""

    def __init__(self, tau_model="global", phi_model="global",
                 phi_s2ss_model=None, tau_quantile=None,
                 phi_ss_quantile=None, phi_s2ss_quantile=None):
        if not self.NGA_EAST_TABLE:
            raise NotImplementedError("NGA East Fixed-Table GMPE requires "
                                      "input table")
        super().__init__(
            gmpe_table=self.NGA_EAST_TABLE,
            tau_model=tau_model, phi_model=phi_model,
            phi_s2ss_model=phi_s2ss_model, tau_quantile=tau_quantile,
            phi_ss_quantile=phi_ss_quantile,
            phi_s2ss_quantile=phi_s2ss_quantile)

class NGAEastBaseGMPETotalSigma(NGAEastBaseGMPE):
    """
    The Al Atik (2015) standard deviation model defines mean and quantiles
    for the inter- and intra-event residuals. However, it also defines
    separately a total standard deviation model with expectation and quantiles.
    As the inter- and intra-event quantile cannot be recovered unambiguously
    from the total standard deviation quantile this form of the model is
    defined only for total standard deviation. Most likely it is this form
    that would be used for seismic hazard analysis.

    :param SIGMA:
        Total standard deviation model at quantile

    :param list magnitude_limits:
        Magnitude limits corresponding to the selected standard deviation
        model

    :param list tau_keys:
        Keys for the tau values corresponding to the selected standard
        deviation model
    """
    DEFINED_FOR_STANDARD_DEVIATION_TYPES = set((const.StdDev.TOTAL,))

    def __init__(self, gmpe_table, tau_model="global", phi_model="global",
                 phi_s2ss_model=None, sigma_quantile=None):
        """
        Instantiates the model call the BaseNGAEastModel to return the
        expected TAU, PHI_SS and PHI_S2SS values then uses these to
        calculate the expected total standard deviation and its variance
        according to equations 5.15, 5.16 and/or 5.18 and 5.19 of Al Atik
        (2015)

        :param float sigma_quantile:
            Quantile of the epistemic uncertainty model for the total
            standard deviation. Should be float between 0 and 1, or None (mean
            value taken)
        """
        super().__init__(gmpe_table=gmpe_table,
                         tau_model=tau_model, phi_model=phi_model,
                         phi_s2ss_model=phi_s2ss_model, tau_quantile=None,
                         phi_ss_quantile=None, phi_s2ss_quantile=None)
        # Upon instantiation the TAU, PHI_SS, and PHI_S2SS objects contain
        # the mean values
        self.SIGMA = None
        self.magnitude_limits = []
        self.tau_keys = []
        self._get_sigma_at_quantile(sigma_quantile)

    def get_stddevs(self, mag, imt, stddev_types, num_sites):
        """
        Returns the total standard deviation
        """
        stddevs = []
        for stddev_type in stddev_types:
            assert stddev_type in self.DEFINED_FOR_STANDARD_DEVIATION_TYPES
            if stddev_type == const.StdDev.TOTAL:
                sigma = self._get_total_sigma(imt, mag)
                stddevs.append(sigma + np.zeros(num_sites))
        return stddevs

    def _get_sigma_at_quantile(self, sigma_quantile):
        """
        Calculates the total standard deviation at the specified quantile
        """
        # Mean mean is found in self.TAU. Get the variance in tau
        tau_std = TAU_SETUP[self.tau_model]["STD"]
        # Mean phiss is found in self.PHI_SS. Get the variance in phi
        phi_std = deepcopy(self.PHI_SS.sa_coeffs)
        phi_std.update(self.PHI_SS.non_sa_coeffs)
        for key in phi_std:
            phi_std[key] = {"a": PHI_SETUP[self.phi_model][key]["var_a"],
                            "b": PHI_SETUP[self.phi_model][key]["var_b"]}
        if self.ergodic:
            # IMT list should be taken from the PHI_S2SS_MODEL
            imt_list = list(
                PHI_S2SS_MODEL[self.phi_s2ss_model].non_sa_coeffs.keys())
            imt_list += \
                list(PHI_S2SS_MODEL[self.phi_s2ss_model].sa_coeffs.keys())
        else:
            imt_list = phi_std.keys()
        phi_std = CoeffsTable(sa_damping=5, table=phi_std)
        tau_bar, tau_std = self._get_tau_vector(self.TAU, tau_std, imt_list)
        phi_bar, phi_std = self._get_phi_vector(self.PHI_SS, phi_std, imt_list)
        sigma = {}
        # Calculate the total standard deviation
        for imt in imt_list:
            sigma[imt] = {}
            for i, key in enumerate(self.tau_keys):
                # Calculates the expected standard deviation
                sigma_bar = np.sqrt(tau_bar[imt][i] ** 2. +
                                    phi_bar[imt][i] ** 2.)
                # Calculated the variance in the standard deviation
                sigma_std = np.sqrt(tau_std[imt][i] ** 2. +
                                    phi_std[imt][i] ** 2.)
                # The keys swap from tau to sigma
                new_key = key.replace("tau", "sigma")
                if sigma_quantile is not None:
                    sigma[imt][new_key] =\
                        _at_percentile(sigma_bar, sigma_std, sigma_quantile)
                else:
                    sigma[imt][new_key] = sigma_bar
                self.tau_keys[i] = new_key
        self.SIGMA = CoeffsTable(sa_damping=5, table=sigma)

    def _get_tau_vector(self, tau_mean, tau_std, imt_list):
        """
        Gets the vector of mean and variance of tau values corresponding to
        the specific model and returns them as dictionaries
        """
        self.magnitude_limits = MAG_LIMS_KEYS[self.tau_model]["mag"]
        self.tau_keys = MAG_LIMS_KEYS[self.tau_model]["keys"]
        t_bar = {}
        t_std = {}
        for imt in imt_list:
            t_bar[imt] = []
            t_std[imt] = []
            for mag, key in zip(self.magnitude_limits, self.tau_keys):
                t_bar[imt].append(
                    TAU_EXECUTION[self.tau_model](imt, mag, tau_mean))
                t_std[imt].append(
                    TAU_EXECUTION[self.tau_model](imt, mag, tau_std))
        return t_bar, t_std

    def _get_phi_vector(self, phi_mean, phi_std, imt_list):
        """
        Gets the vector of mean and variance of phi values corresponding to
        the specific model and returns them as dictionaries
        """
        p_bar = {}
        p_std = {}

        for imt in imt_list:
            p_bar[imt] = []
            p_std[imt] = []
            for mag in self.magnitude_limits:
                phi_ss_mean = get_phi_ss(imt, mag, phi_mean)
                phi_ss_std = get_phi_ss(imt, mag, phi_std)
                if self.ergodic:
                    # Add on the phi_s2ss term according to Eqs. 5.15 and 5.16
                    # of Al Atik (2015)
                    phi_ss_mean = np.sqrt(
                        phi_ss_mean ** 2. +
                        PHI_S2SS_MODEL[self.phi_s2ss_model][imt]["mean"] ** 2.
                        )
                    phi_ss_std = np.sqrt(
                        phi_ss_std ** 2. +
                        PHI_S2SS_MODEL[self.phi_s2ss_model][imt]["var"] ** 2.
                        )
                p_bar[imt].append(phi_ss_mean)
                p_std[imt].append(phi_ss_std)
        return p_bar, p_std

    def _get_total_sigma(self, imt, mag):
        """
        Returns the estimated total standard deviation for a given intensity
        measure type and magnitude
        """
        C = self.SIGMA[imt]
        if mag <= self.magnitude_limits[0]:
            # The CENA constant model is always returned here
            return C[self.tau_keys[0]]
        elif mag > self.magnitude_limits[-1]:
            return C[self.tau_keys[-1]]
        else:
            # Needs interpolation
            for i in range(len(self.tau_keys) - 1):
                l_m = self.magnitude_limits[i]
                u_m = self.magnitude_limits[i + 1]
                if mag > l_m and mag <= u_m:
                    return ITPL(mag,
                                C[self.tau_keys[i + 1]],
                                C[self.tau_keys[i]],
                                l_m,
                                u_m - l_m)

class NGAEastGMPETotalSigma(NGAEastBaseGMPETotalSigma):
    """
    Subclass of the :class:`NGAEastBaseGMPETotalSigma` for the cases when the
    GMPE table is fixed. This forms the main base-class for the total sigma
    version of the core set of NGA East models
    """
    NGA_EAST_TABLE = ""

    def __init__(self, tau_model="global", phi_model="global",
                 phi_s2ss_model=None, sigma_quantile=None):
        """
        Instantiates the GMPE without the hdf5 table fort the median values
        """
        if not self.NGA_EAST_TABLE:
            raise NotImplementedError("NGA East Fixed-Table GMPE requires "
                                      "input table")
        super().__init__(
            self.NGA_EAST_TABLE, tau_model=tau_model, phi_model=phi_model,
            phi_s2ss_model=phi_s2ss_model, sigma_quantile=sigma_quantile)

# /////////////////////////////////////////////////////////////////////////////
# Now to start adding the actual NGA East GMPEs
# /////////////////////////////////////////////////////////////////////////////
BASE_PATH = os.path.join(os.path.dirname(__file__), "nga_east_tables")

# Boore (2015) suite

class Boore2015NGAEastA04(NGAEastGMPE):
    """
    Boore (2015) NGA East GMPE using the Atkinson (2004) attenuation model

    Boore, DM (2015) "Point-Source Stochastic-Method Simulations of
    Ground Motions for the PEER NGA-East Project", in "NGA-East: Median
    Ground Motion Models for the Central and Eastern North America Region",
    PEER Report 2015/04, Pacific Earthquake Engineering Research Center
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_BOORE_A04_J15_Adjusted.hdf5")

class Boore2015NGAEastA04TotalSigma(NGAEastGMPETotalSigma):
    """
    Boore (2015) NGA East GMPE using the Atkinson (2004) attenuation model
    for use with the total sigma aleatory uncertainty model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_BOORE_A04_J15_Adjusted.hdf5")

class Boore2015NGAEastAB14(NGAEastGMPE):
    """
    Boore (2015) NGA East GMPE using the Atkinson & Boore (2014) attenuation
    model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_BOORE_AB14_J15_Adjusted.hdf5")

class Boore2015NGAEastAB14TotalSigma(NGAEastGMPETotalSigma):
    """
    Boore (2015) NGA East GMPE using the Atkinson & Boore (2014) attenuation
    model for use with the total sigma aleatory uncertainty model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_BOORE_AB14_J15_Adjusted.hdf5")

class Boore2015NGAEastAB95(NGAEastGMPE):
    """
    Boore (2015) NGA East GMPE using the Atkinson & Boore (1995) attenuation
    model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_BOORE_AB95_J15_Adjusted.hdf5")

class Boore2015NGAEastAB95TotalSigma(NGAEastGMPETotalSigma):
    """
    Boore (2015) NGA East GMPE using the Atkinson & Boore (1995) attenuation
    model for use with the total sigma aleatory uncertainty model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_BOORE_AB95_J15_Adjusted.hdf5")

class Boore2015NGAEastBCA10D(NGAEastGMPE):
    """
    Boore (2015) NGA East GMPE using the Boore et al (2010) attenuation
    model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_BOORE_BCA10D_J15_Adjusted.hdf5")

class Boore2015NGAEastBCA10DTotalSigma(NGAEastGMPETotalSigma):
    """
    Boore (2015) NGA East GMPE using the Boore et al (2010) attenuation
    model for use with the total sigma aleatory uncertainty model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_BOORE_BCA10D_J15_Adjusted.hdf5")

class Boore2015NGAEastBS11(NGAEastGMPE):
    """
    Boore (2015) NGA East GMPE using the Boatwright and Seekins (2011)
    attenuation model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_BOORE_BS11_J15_Adjusted.hdf5")

class Boore2015NGAEastBS11TotalSigma(NGAEastGMPETotalSigma):
    """
    Boore (2015) NGA East GMPE using the Boatwright and Seekins (2011)
    attenuation model for use with the total sigma aleatory uncertainty model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_BOORE_BS11_J15_Adjusted.hdf5")

class Boore2015NGAEastSGD02(NGAEastGMPE):
    """
    Boore (2015) NGA East GMPE using the Silva et al (2002) attenuation model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_BOORE_SGD02_J15_Adjusted.hdf5")

class Boore2015NGAEastSGD02TotalSigma(NGAEastGMPETotalSigma):
    """
    Boore (2015) NGA East GMPE using the Silva et al (2002) attenuation model
    for use with the total sigma aleatory uncertainty model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_BOORE_SGD02_J15_Adjusted.hdf5")

# Darragh, Abrahamson, Silva and Gregor (2015) suite

class DarraghEtAl2015NGAEast1CCSP(NGAEastGMPE):
    """
    NGA East model of Darragh et al. (2015) adopting the single-corner
    constant stress parameter (1CCSP)

    Darragh, RB, Abrahamson, NA, Wilva, WJ, Gregor, N (2015) "Development of
    Hard Rock Ground Motion Models for Region 2 of Central and Eastern
    North America" in ...
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_DARRAGH_1CCSP.hdf5")

class DarraghEtAl2015NGAEast1CCSPTotalSigma(NGAEastGMPETotalSigma):
    """
    NGA East model of Darragh et al. (2015) adopting the single-corner
    constant stress parameter (1CCSP) for use with the total sigma aleatory
    uncertainty model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_DARRAGH_1CCSP.hdf5")

class DarraghEtAl2015NGAEast1CVSP(NGAEastGMPE):
    """
    NGA East model of Darragh et al. (2015) adopting the single-corner
    variable stress parameter (1CVSP)
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_DARRAGH_1CVSP.hdf5")

class DarraghEtAl2015NGAEast1CVSPTotalSigma(NGAEastGMPETotalSigma):
    """
    NGA East model of Darragh et al. (2015) adopting the single-corner
    variable stress parameter (1CVSP) for use with the total sigma aleatory
    uncertainty model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_DARRAGH_1CVSP.hdf5")

class DarraghEtAl2015NGAEast2CCSP(NGAEastGMPE):
    """
    NGA East model of Darragh et al. (2015) adopting the two-corner
    constant stress parameter (2CCSP)
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_DARRAGH_2CCSP.hdf5")

class DarraghEtAl2015NGAEast2CCSPTotalSigma(NGAEastGMPETotalSigma):
    """
    NGA East model of Darragh et al. (2015) adopting the two-corner
    constant stress parameter (2CCSP) for use with the total sigma aleatory
    uncertainty model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_DARRAGH_2CCSP.hdf5")

class DarraghEtAl2015NGAEast2CVSP(NGAEastGMPE):
    """
    NGA East model of Darragh et al. (2015) adopting the two-corner
    variable stress parameter (1CVSP)
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_DARRAGH_2CVSP.hdf5")

class DarraghEtAl2015NGAEast2CVSPTotalSigma(NGAEastGMPETotalSigma):
    """
    NGA East model of Darragh et al. (2015) adopting the two-corner
    variable stress parameter (2CVSP) for use with the total sigma aleatory
    uncertainty model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_DARRAGH_2CVSP.hdf5")

# Yenier & Atkinson (2015)

class YenierAtkinson2015NGAEast(NGAEastGMPE):
    """
    NGA East Model of Yenier & Atkinson (2015)
    Yenier, E and Atkinson, GA (2015) "Regionally-Adjustable Generic Ground-
    Motion Prediction Equation based on Equivalent Point-Source Simulations:
    Application to Central and Eastern North America" in PEER 2015/04, Chapter
    4
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_YENIER_ATKINSON.hdf5")

class YenierAtkinson2015NGAEastTotalSigma(NGAEastGMPETotalSigma):
    """
    NGA East Model of Yenier & Atkinson (2015) for use with the total sigma
    aleatory uncertainty model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_YENIER_ATKINSON.hdf5")

# Pezeschk et al. (2015)

class PezeschkEtAl2015NGAEastM1SS(NGAEastGMPE):
    """
    NGA East Model of Pezeschk et al (2015) for the large-M simulation scaling

    Pezeschk, S., Zandieh, A., Campbell, KW and Tavakoli B (2015) "Ground-
    Motion Prediction Equations for Eastern North America using a Hybrid
    Empirical Method" in PEER 2015/04, Chapter 5
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_PEZESCHK_M1SS.hdf5")

class PezeschkEtAl2015NGAEastM1SSTotalSigma(NGAEastGMPETotalSigma):
    """
    NGA East Model of Pezeschk et al (2015) for the large-M simulation scaling
    for use with the total sigma aleatory uncertainty model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_PEZESCHK_M1SS.hdf5")

class PezeschkEtAl2015NGAEastM2ES(NGAEastGMPE):
    """
    NGA East Model of Pezeschk et al (2015) for the large-M empirical scaling
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_PEZESCHK_M2ES.hdf5")

class PezeschkEtAl2015NGAEastM2ESTotalSigma(NGAEastGMPETotalSigma):
    """
    NGA East Model of Pezeschk et al (2015) for the large-M empirical scaling
    for use with the total sigma aleatory uncertainty model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_PEZESCHK_M2ES.hdf5")

# Frankel (2015)

class Frankel2015NGAEast(NGAEastGMPE):
    """
    NGA East Model of Frankel (2015) for application to Central & Eastern
    United States

    Frankel, A (2015) "Ground-Motion Predictions for Eastern North American
    Earthquakes Using Hybrid Broadband Seismograms from Finite-Fault
    Simulation with Constant Stress-Drop Scaling" in PEER 2015/04, Chapter 6
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_FRANKEL_J15.hdf5")

class Frankel2015NGAEastTotalSigma(NGAEastGMPETotalSigma):
    """
    NGA East Model of Frankel (2015) for application to Central & Eastern
    United States for use with the total sigma aleatory uncertainty model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_FRANKEL_J15.hdf5")

# Shahjouei & Pezeschk (2015)

class ShahjoueiPezeschk2015NGAEast(NGAEastGMPE):
    """
    NGA East Model of Shahjouei & Pezeschk (2015) for application to Central &
    Eastern United States

    Shajouei, A and Pezeschk, S (2015) "Hybrid Empirical Ground-Motion Model
    for Central and Eastern North America using Hybrid Broadband Simulations
    and NGA-West2 GMPEs" in PEER 2015/04, Chapter 7
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_SHAHJOUEI_PEZESCHK.hdf5")

class ShahjoueiPezeschk2015NGAEastTotalSigma(NGAEastGMPETotalSigma):
    """
    NGA East Model of Shahjouei & Pezeschk (2015) for application to Central &
    Eastern United States, for use with the total sigma aleatory uncertainty
    model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_SHAHJOUEI_PEZESCHK.hdf5")

# Al Noman and Cramer (2015)

class AlNomanCramer2015NGAEast(NGAEastGMPE):
    """
    NGA East Model of Al Noman & Cramer (2015) for application to Central &
    Eastern United States

    Al Noman & Cramer (2015) "Empirical Ground-Motion Prediction Equations for
    Eastern North America" in PEER 2015/04, Chapter 8
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_ALNOMAN_CRAMER.hdf5")

class AlNomanCramer2015NGAEastTotalSigma(NGAEastGMPETotalSigma):
    """
    NGA East Model of Al Noman & Cramer (2015) for application to Central &
    Eastern United States, for use with the total sigma aleatory uncertainty
    model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_ALNOMAN_CRAMER.hdf5")

# Graizer (2015)

class Graizer2015NGAEast(NGAEastGMPE):
    """
    NGA East Model of Graizer (2015) for application to Central & Eastern
    United States

    Graizer, V (2015) "Ground-Motion Prediction Equations for the Central and
    Eastern United States" in PEER 2015/04, Chapter 9
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_GRAIZER.hdf5")

class Graizer2015NGAEastTotalSigma(NGAEastGMPETotalSigma):
    """
    NGA East Model of Graizer (2015) for application to Central & Eastern
    United States, for use with the total sigma aleatory uncertainty
    model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_GRAIZER.hdf5")

# Hassani & Atkinson (2015)

class HassaniAtkinson2015NGAEast(NGAEastGMPE):
    """
    NGA East Model of Hassani & Atkinson (2015) for application to Central &
    Eastern United States

    Hassani, B & Atkinson, GA (2015) "Referenced Empirical Ground-Motion Model
    for Eastern North America" in PEER 2015/04, Chapter 10
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_HASSANI_ATKINSON.hdf5")

class HassaniAtkinson2015NGAEastTotalSigma(NGAEastGMPETotalSigma):
    """
    NGA East Model of Hassani & Atkinson (2015) for application to Central &
    Eastern United States, for use with the total sigma aleatory uncertainty
    model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_HASSANI_ATKINSON.hdf5")

# Hollenback et al. (2015)

class HollenbackEtAl2015NGAEastGP(NGAEastGMPE):
    """
    NGA East Model of Hollenback et al (2015) for application to Central &
    Eastern United States using the GP Finite-Fault model

    Hollenback, J, Keuhn, N, Goulet, CA and Abrahamson, NA (2015) "PEER NGA-
    East Median Ground Motion Models" in PEER 2015/04, Chapter 11
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_PEER_GP.hdf5")

class HollenbackEtAl2015NGAEastGPTotalSigma(NGAEastGMPETotalSigma):
    """
    NGA East Model of Hollenback et al (2015) for application to Central &
    Eastern United States, for use with the total sigma aleatory uncertainty
    model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_PEER_GP.hdf5")

class HollenbackEtAl2015NGAEastEX(NGAEastGMPE):
    """
    NGA East Model of Hollenback et al (2015) for application to Central &
    Eastern United States using the EXSIM Finite-Fault model

    Hollenback, J, Keuhn, N, Goulet, CA and Abrahamson, NA (2015) "PEER NGA-
    East Median Ground Motion Models" in PEER 2015/04, Chapter 11
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_PEER_EX.hdf5")

class HollenbackEtAl2015NGAEastEXTotalSigma(NGAEastGMPETotalSigma):
    """
    NGA East Model of Hollenback et al (2015) for application to Central &
    Eastern United States, for use with the total sigma aleatory uncertainty
    model
    """
    NGA_EAST_TABLE = os.path.join(BASE_PATH,
                                  "NGAEast_PEER_EX.hdf5")


class Car:
    def __init__(self, model, manufacturer, year):
        self.model = model
        self.manufacturer = manufacturer
        self.year = year

    def descriptive_message(self):
        print(f"my {self.model} was created by {self.manufacturer} in {self.year}")

mycar = Car("Dena+", "irankhodro", 1400 )
mycar.descriptive_message()
mycar.model = "207i"
mycar.year = 1398
mycar.descriptive_message()


from django.urls import path
from .views import PartnerUpdate, ShopStateView, OrdersView

app_name = 'partners'

urlpatterns = [
    path('partner/update/', PartnerUpdate.as_view(), name='update_price'),
    path('partner/state/', ShopStateView.as_view(), name='shop_state'),
    path('partner/orders/', OrdersView.as_view(), name='orders'),
]


# -*- coding=utf-8 -*-
"""
@file  : _dao
@date  : 2016-12-02
@author: GongXiaobo
"""
from freetime5.util import fttime, ftlog
from freetime5.util.ftcache import lfu_alive_cache
from hall5.entity import hallconf
from tuyoo5.core import tyconfig
from tuyoo5.core import tyglobal
from tuyoo5.core.tyconfig import TyCachedConfig

_DEBUG = 1

if _DEBUG:
    def debug(*argl, **argd):
        ftlog.debug(*argl, **argd)
else:
    def debug(*argl, **argd):
        pass

MAINCONF = None
SUBCONFS = {}
PRODUCT_MAP = {}

def reloadConfig():
    if _DEBUG:
        debug('hallexchange.reloadConfig in')
    mainconf = TyCachedConfig('exchange', tyglobal.gameId())
    extabs = mainconf.getScConfig().get('extabs', {})
    if _DEBUG:
        debug('hallexchange.reloadConfig mainconf=', mainconf)
    names = set()
    for _, tab in extabs.iteritems():
        names.add(tab['name'])
    subconfs = {}
    product_map = {}
    for name in names:
        subconfs[name] = TyCachedConfig('exchange:%s' % (name), tyglobal.gameId())

        #add product_map info added by lixi
        if subconfs[name]:
            confs = subconfs[name].getScConfig().get('exitems', {})
            for key, conf in confs.items():
                if key:
                    product_map[key] = confs[key]['productId']
                    if _DEBUG:
                        debug('hallexchange _private _conf reloadConfig %s', conf)
                else:
                    if _DEBUG:
                        debug('hallexchange _private _conf reloadConfig %s', conf)

    global MAINCONF, SUBCONFS, PRODUCT_MAP
    MAINCONF = mainconf
    SUBCONFS = subconfs
    PRODUCT_MAP = product_map
    # 清理缓存
    getExchangeQueryUiTabs.clear()
    _getExchangeQueryUiItems.clear()
    if _DEBUG:
        debug('hallexchange.reloadConfig out')

@lfu_alive_cache(maxsize=1024, cache_key_args_index=0, alive_second=60)
def getExchangeQueryUiTabs(clientId):
    '''
    变化不频繁，每分钟重新计算一次
    '''
    if _DEBUG:
        debug('getExchangeQueryUiTabs', type(clientId), clientId)
    bannerIds = []
    tabIds = []
    confs = MAINCONF.getConfigByClientId(clientId)
    if _DEBUG:
        debug('getExchangeQueryUiTabs', clientId, confs)
    if confs:
        bannerIds = hallconf.filterStartStopTime(confs['exbanners'], MAINCONF, 'exbanners')
        tabIds = hallconf.filterStartStopTime(confs['extabs'], MAINCONF, 'extabs')
    if _DEBUG:
        debug('getExchangeQueryUiTabs', clientId, bannerIds, tabIds)
    return bannerIds, tabIds

def getExchangeQueryUiItems(tabName, pageNum, clientId):
    if _DEBUG:
        debug('getExchangeQueryUiItems', tabName, pageNum, clientId)
    clientId = tyconfig.clientIdToNumber(clientId)
    items = _getExchangeQueryUiItems('%s:%s' % (tabName, clientId))
    pagesize = MAINCONF.getScConfig().get('pagesize', 20)
    index = pageNum * pagesize
    page = items[index: index + pagesize]
    if _DEBUG:
        debug('getExchangeQueryUiItems', tabName, pageNum, clientId, page)
    return page

@lfu_alive_cache(maxsize=4096, cache_key_args_index=0, alive_second=60)
def _getExchangeQueryUiItems(key):
    '''
    变化不频繁，每分钟重新计算一次
    '''
    if _DEBUG:
        debug('_getExchangeQueryUiItems', key)
    itemIds = []
    tks = key.split(':')
    tabName, clientId = tks[0], int(tks[1])
    tabConf = SUBCONFS.get(tabName, None)
    if tabConf:
        confs = tabConf.getConfigByClientId(clientId)
        if _DEBUG:
            debug('_getExchangeQueryUiItems ! tabName=', tabName, 'clientId=', clientId, 'confs=', confs)
        if confs:
            itemIds = hallconf.filterStartStopTime(confs['exitems'], tabConf, 'exitems')
        else:
            if _DEBUG:
                debug('_getExchangeQueryUiItems not config ! tabName=', tabName, 'clientId=', clientId)
    else:
        if _DEBUG:
            debug('_getExchangeQueryUiItems not config ! tabName=', tabName)
    if _DEBUG:
        debug('_getExchangeQueryUiItems', key, itemIds)
    return itemIds

def getItemInfo(tabName, itemId):
    if _DEBUG:
        debug('getItemInfo', tabName, itemId)
    info = None
    tabConf = SUBCONFS.get(tabName, None)
    if tabConf:
        conf = tabConf.getScConfig().get('exitems', {}).get(itemId)
        if conf:
            ct = fttime.formatTimeSecond()
            startTime = conf['startTime']
            endTime = conf['endTime']
            if ct < startTime or ct > endTime:
                info = None
            else:
                info  = conf
                if _DEBUG:
                    debug('getItemInfo item timeout', tabName, itemId, ct, conf)
        else:
            if _DEBUG:
                debug('getItemInfo item notfound', tabName, itemId, tabConf.getScConfig())
    else:
        if _DEBUG:
            debug('getItemInfo tabName notfound', tabName, itemId, SUBCONFS.keys())

    if _DEBUG:
        debug('getItemInfo', tabName, itemId, info)
    return info


import argparse
import cmd2
import typing as T
from cmd2 import CommandSet, with_argparser, with_category, with_default_category

# Required to register the apps
from .edit.commands import EditCommands
from .edit.interactive_requirement import AppRequirement
from .edit.interactive_products import AppProduct
from .edit.interactive_constraints import AppConstraint
from .edit.interactive_maturity import AppMaturity
from .analyze.app_analyze import AppAnalyze
# from .edit import AppProduct
# from .edit import AppConstraint

from bop.gui.window import BopMainWindow,BopMainWindowThread
from .env import AppEnv

from threading import Thread, Semaphore
from queue import Empty
from time import sleep

class Interactive(cmd2.Cmd):
	def __init__(self, *args, **kwargs):
		# gotta have this or neither the plugin or cmd2 will initialize
		super().__init__(*args, allow_cli_args=False, auto_load_commands=True,
						 persistent_history_file="~/.bop.history",persistent_history_length=100, **kwargs)
		self.prompt = "Bop>"

		self.intro = f"Welcome to the Build Objects Properly (BOP) tool.\n"
		self.gui_thread : BopMainWindowThread = None
		self.monitor_thread : BopMainWindowThread = None

		self.register_postloop_hook(self._ensure_gui_cleanup)
		self.register_precmd_hook(self._get_command_lock)
		self.register_cmdfinalization_hook(self._release_command_lock)

		self._cmd_lock = Semaphore()

	def _start_monitor_thread(self):
		def monitor():
			while True:
				sleep(0.1)
				with AppEnv().cmd_queue_lock :
					try :
						while True:
							elt = AppEnv().cmd_control_queue.get(block=False)
							if elt == "exit" :
								while not AppEnv().cmd_control_queue.empty() :
									AppEnv().cmd_control_queue.get_nowait()
								return
							print(elt)
							self.onecmd_plus_hooks(elt,add_to_history=False)
							print(self.prompt,end="")
					except Empty:
						pass

		if self.monitor_thread is None or not self.monitor_thread.is_alive() :
			self.monitor_thread = Thread(target=monitor)
			self.monitor_thread.start()

	def _get_command_lock(self, _ : cmd2.plugin.PrecommandData) -> cmd2.plugin.PrecommandData:
		self._cmd_lock.acquire()
		return _

	def _release_command_lock(self, _ : cmd2.plugin.CommandFinalizationData) -> cmd2.plugin.CommandFinalizationData:
		self._cmd_lock.release()
		return _

	def _ensure_gui_cleanup(self) -> None:
		if self.gui_thread is not None and self.gui_thread.is_alive():
			self.poutput("Cleanup the GUI")
			self.gui_thread.stop()
			self.gui_thread.join()

	# Add "aliases"
	do_exit = cmd2.Cmd.do_quit
	do_source = cmd2.Cmd.do__relative_run_script

	def do_gui(self,_):
		"""Start the GUI"""
		if self.gui_thread is not None and self.gui_thread.is_alive():
			self.poutput("Await for already opened window to be closed.")
			self.gui_thread.join()

		def gui_function() :
			root = BopMainWindow()
			root.mainloop()

		self.poutput("Starting BOP GUI.")
		self._start_monitor_thread()
		self.gui_thread = BopMainWindowThread()
		self.gui_thread.start()

	def update_prompt(self):
		self.prompt = f"Bop {AppEnv().db.name}> "
		self.intro = f"Welcome to the Build Objects Properly (BOP) tool.\n" \
					 f"Database is {AppEnv().db.name}"

	def _default_subcommand_stub(self, command : str, ns : argparse.Namespace):
		handler = ns.cmd2_handler.get()
		if handler is not None:
			# Call whatever subcommand function was selected
			handler(ns)
		else:
			# No subcommand was provided, so call help
			self.poutput('This command does nothing without sub-parsers registered')
			self.do_help(command)


import torch
import torch.nn as nn
import torch.nn.functional as F

import dgl
from dgl.nn.pytorch.glob import SumPooling, AvgPooling, MaxPooling

"""
    GIN: Graph Isomorphism Networks
    HOW POWERFUL ARE GRAPH NEURAL NETWORKS? (Keyulu Xu, Weihua Hu, Jure Leskovec and Stefanie Jegelka, ICLR 2019)
    https://arxiv.org/pdf/1810.00826.pdf
"""

from layers.gin_layer import GINLayer, ApplyNodeFunc, MLP

activations = {
    'relu': nn.ReLU(),
    'tanh': nn.Tanh(),
    'identity': nn.Identity()
}

class GINNet(nn.Module):

    def __init__(self, net_params):
        super().__init__()
        in_dim = net_params['in_dim']
        hidden_dim = net_params['hidden_dim']
        out_dim = net_params['out_dim']
        dropout = net_params['dropout']
        self.n_layers = net_params['L']
        n_mlp_layers = net_params['n_mlp_GIN']               # GIN
        learn_eps = net_params['learn_eps_GIN']              # GIN
        neighbor_aggr_type = net_params['neighbor_aggr_GIN'] # GIN
        readout = net_params['readout']                      # this is graph_pooling_type
        batch_norm = net_params['batch_norm']
        residual = net_params['residual']
        activation_name = net_params['activation']

        # List of MLPs
        self.ginlayers = torch.nn.ModuleList()

        self.embedding_h = nn.Linear(in_dim, hidden_dim)

        for layer in range(self.n_layers - 1):
            mlp = MLP(n_mlp_layers, hidden_dim, hidden_dim, hidden_dim, activations[activation_name])

            self.ginlayers.append(GINLayer(ApplyNodeFunc(mlp), neighbor_aggr_type,
                                           dropout, batch_norm, residual, 0, learn_eps, activations[activation_name]))

        self.ginlayers.append(GINLayer(ApplyNodeFunc(MLP(n_mlp_layers, hidden_dim, hidden_dim, out_dim, activations[activation_name])), neighbor_aggr_type,
                                dropout, batch_norm, residual, 0, learn_eps, activations[activation_name]))

    def forward(self, g, h, e, intermediate=None):
        h = self.embedding_h(h)

        for i in range(self.n_layers):
            h = self.ginlayers[i](g, h, intermediate)

            if intermediate is not None:
                intermediate(h, 'out')

        return h

    def loss(self, scores, targets):
        loss = nn.L1Loss()(scores, targets)
        return loss


from django.urls import path
from .views import index, register, sign_in, sign_out, logged_in, send_a_post,delete_a_post

app_name = 'usermodel'

#do not forget adding app_name

urlpatterns = [
    path('', index, name = 'index_page'),
    path('registration/', register, name = 'registration_page'),
    path('login/', sign_in, name = 'login_page'),
    path('logout/', sign_out, name = 'logout_page'),
    path('success/', logged_in, name = 'successful_login_page'),
    path('posts/', send_a_post, name = 'post_page'),
    path('posts/delete/<int:post_id>', delete_a_post, name = 'delete_post_page'),
]


class Solution:
  def dfs(self, i, colorCode, bags, colors):
    colors[i] = colorCode
    for j in bags[i]:
      if j in colors and colors[j] == colorCode:
        return False
      elif j not in colors:
        tmp = self.dfs(j, -colorCode, bags, colors)
        if not tmp:
          return False
    return True
  def possibleBipartition(self, N: int, dislikes: List[List[int]]) -> bool:
    bag = [[] for _ in range(N+1)]
    for i, j in dislikes:
      bag[i].append(j)
      bag[j].append(i)
    colors = {}
    for i, j in dislikes:
      if i not in colors and not(self.dfs(i, 1, bag, colors)):
        return False
    return True


"""
Code for accessing the data in the database easily.
"""
import math
import os
import shutil
import patoolib
import requests
import torch
import numpy as np
import pandas as pd
import imageio
from skimage import transform
import sklearn.utils
import gdown
from torch.utils.data import Dataset

from utility import to_normalized_range, seed_all

database_directory = '../Steering Angle Database/Steering Angle Database/driving_dataset/driving_dataset/'

class SteeringAngleDataset(Dataset):
    """The dataset class for the age estimation application."""
    def __init__(self, start=None, end=None, seed=None, batch_size=None):
        seed_all(seed)
        self.dataset_path = database_directory
        #with open(os.path.join(self.dataset_path, 'data.txt')) as f:
        meta = pd.read_csv(os.path.join(self.dataset_path, 'data.txt'),header=None, delimiter =" ")
        #meta = pd.read_pickle(os.path.join(self.dataset_path, 'meta.pkl'))
        meta = sklearn.utils.shuffle(meta, random_state=seed)  # Shuffles only first axis.
        image_names = meta.iloc[:, 0].values
        angles = meta.iloc[:, 1].values
        #print(angles)
        #from sklearn import preprocessing
        #min_max_scaler = preprocessing.MinMaxScaler()
        #angles = min_max_scaler.fit_transform(angles.reshape(-1, 1)).reshape(-1)
        self.image_names = np.array(image_names[start:end])
        self.angles = np.array(angles[start:end], dtype=np.float32)
        # Force full batch sizes
        if self.image_names.shape[0] < batch_size:
            repeats = math.ceil(batch_size / self.image_names.shape[0])
            self.image_names = np.repeat(self.image_names, repeats)
            self.angles = np.repeat(self.angles, repeats)
        self.length = self.angles.shape[0]
        self.image_size = 128

    def __len__(self):
        return self.length

    def __getitem__(self, index):
        image_name = self.image_names[index]
        image = np.load(os.path.join(self.dataset_path, image_name.replace('.jpg', '.npy')))
        image = torch.tensor(image.astype(np.float32))
        image = to_normalized_range(image)
        angle = self.angles[index]
        angle = torch.tensor(angle, dtype=torch.float32)
        return image, angle

class SteeringAngleDatabaseProcessor:
    """A class for preparing the Sully Chen steering angle database."""
    def __init__(self, preprocessed_image_size=128):
        self.preprocessed_image_size = preprocessed_image_size

    def download_and_preprocess(self):
        """Downloads and preprocesses the database."""
        print('Preparing steering angle database.')
        print('Downloading...')
        #self.download()
        print('Preprocessing...')
        self.preprocess()

    @staticmethod
    def download():
        """Downloads the database."""
        if os.path.exists(database_directory):
            shutil.rmtree(database_directory)
        os.makedirs(database_directory)
        SteeringAngleDatabaseProcessor.download_file_from_google_drive('1M1AOX3T5GqYMpU6y13dN24GQ22ILTlJK',
                                                                       os.path.join(database_directory, 'temporary'))
        patoolib.extract_archive(os.path.join(database_directory, 'temporary'), outdir=database_directory)
        default_directory_name = 'driving_dataset'
        files = os.listdir(os.path.join(database_directory, default_directory_name))
        for file_ in files:
            if not file_.startswith('.'):
                shutil.move(os.path.join(database_directory, default_directory_name, file_), database_directory)
        shutil.rmtree(os.path.join(database_directory, default_directory_name))
        os.remove(os.path.join(database_directory, 'temporary'))

    @staticmethod
    def download_file_from_google_drive(id_, destination):
        url = 'https://drive.google.com/uc?id=' + id_
        output = destination
        gdown.download(url, output, quiet=False)

    def preprocess(self):
        """Preprocesses the database to the format needed by the network."""
        meta_file_path = os.path.join(database_directory, 'data.txt')
        meta = pd.read_csv(meta_file_path, delimiter=' ', header=None)
        meta = meta[meta[0] != '45567.jpg']  # Corrupt image.
        meta.to_pickle(os.path.join(database_directory, 'meta.pkl'))
        for file_name in meta.iloc[:, 0].values:
            if file_name.endswith('.jpg'):
                file_path = os.path.join(database_directory, file_name)
                image = imageio.imread(file_path).astype(np.uint8)
                image = transform.resize(image, (self.preprocessed_image_size, self.preprocessed_image_size),
                                         preserve_range=True)
                image = image.transpose((2, 0, 1))
                np.save(file_path.replace('.jpg', '.npy'), image)

if __name__ == '__main__':
    SteeringAngleDatabaseProcessor().download_and_preprocess()


import csv

path = 'names.csv'

#csv_file = open(path,'r')

with open(path,'r') as csv_file:
    csv_reader = csv.reader(csv_file)

    for row in csv_reader:
        print(row[0],row[1])


from django.template.loader import render_to_string

from account.models import PublicNetwork

from resource_inventory.models import Vlan

class IDFTemplater:
    """Utility class to create a full Installer Descriptor File (IDF) yaml file."""

    net_names = ["admin", "mgmt", "private", "public"]
    bridge_names = {
        "admin": "br-admin",
        "mgmt": "br-mgmt",
        "private": "br-private",
        "public": "br-public"
    }

    def __init__(self):
        self.networks = {}
        for i, name in enumerate(self.net_names):
            self.networks[name] = {
                "name": name,
                "vlan": -1,
                "interface": i,
                "ip": "10.250." + str(i) + ".0",
                "netmask": 24
            }

    def makeIDF(self, booking):
        """Fill the IDF template with info about the resource."""
        template = "dashboard/idf.yaml"
        info = {}
        info['version'] = "0.1"
        info['net_config'] = self.get_net_config(booking)
        info['fuel'] = self.get_fuel_config(booking)

        return render_to_string(template, context=info)

    def get_net_config(self, booking):
        net_config = {}
        try:
            net_config['oob'] = self.get_oob_net(booking)
        except Exception:
            net_config['oob'] = {}
        try:
            net_config['public'] = self.get_public_net(booking)
        except Exception:
            net_config['public'] = {}

        for net in [net for net in self.net_names if net != "public"]:
            try:
                net_config[net] = self.get_single_net_config(booking, net)
            except Exception:
                net_config[net] = {}

        return net_config

    def get_public_net(self, booking):
        public = {}
        config = booking.opnfv_config
        public_role = config.networks.get(name="public")
        public_vlan = Vlan.objects.filter(network=public_role.network).first()
        public_network = PublicNetwork.objects.get(vlan=public_vlan.vlan_id, lab=booking.lab)
        self.networks['public']['vlan'] = public_vlan.vlan_id
        public['interface'] = self.networks['public']['interface']
        public['vlan'] = public_network.vlan  # untagged??
        public['network'] = public_network.cidr.split("/")[0]
        public['mask'] = public_network.cidr.split("/")[1]
        # public['ip_range'] = 4  # necesary?
        public['gateway'] = public_network.gateway
        public['dns'] = ["1.1.1.1", "8.8.8.8"]

        return public

    def get_oob_net(self, booking):
        net = {}
        hosts = booking.resource.hosts.all()
        addrs = [host.remote_management.address for host in hosts]
        net['ip_range'] = ",".join(addrs)
        net['vlan'] = "native"
        return net

    def get_single_net_config(self, booking, net_name):
        config = booking.opnfv_config
        role = config.networks.get(name=net_name)
        vlan = Vlan.objects.filter(network=role.network).first()
        self.networks[net_name]['vlan'] = vlan.vlan_id
        net = {}
        net['interface'] = self.networks[net_name]['interface']
        net['vlan'] = vlan.vlan_id
        net['network'] = self.networks[net_name]['ip']
        net['mask'] = self.networks[net_name]['netmask']

        return net

    def get_fuel_config(self, booking):
        fuel = {}
        fuel['jumphost'] = {}
        try:
            fuel['jumphost']['bridges'] = self.get_fuel_bridges()
        except Exception:
            fuel['jumphost']['bridges'] = {}

        fuel['network'] = {}
        try:
            fuel['network']['nodes'] = self.get_fuel_nodes(booking)
        except Exception:
            fuel['network']['nodes'] = {}

        return fuel

    def get_fuel_bridges(self):
        return self.bridge_names

    def get_fuel_nodes(self, booking):
        jumphost = booking.opnfv_config.host_opnfv_config.get(
            role__name__iexact="jumphost"
        )
        hosts = booking.resource.hosts.exclude(pk=jumphost.pk)
        nodes = []
        for host in hosts:
            node = {}
            ordered_interfaces = self.get_node_interfaces(host)
            node['interfaces'] = [iface['name'] for iface in ordered_interfaces]
            node['bus_addrs'] = [iface['bus'] for iface in ordered_interfaces]
            nodes.append(node)

        return nodes

    def get_node_interfaces(self, node):
        # TODO: this should sync with pdf ordering
        interfaces = []

        for iface in node.interfaces.all():
            interfaces.append({"name": iface.name, "bus": iface.bus_address})

        return interfaces


x = int(input("Enter a number to find factorial"))
factorial = 1
if x < 0:
    print("There is no factorial for negative numbers")
elif x == 0:
    print("Factorial of zero is 1")
else:
    for i in range(1,x + 1):
        factorial = factorial*i
    print("Factorial of a number", x, "is", factorial)


# import
## batteries
import re
import os
import sys
import gzip
import tempfile
import multiprocessing
import argparse
import logging
import urllib
import tarfile
## 3rd party
import pandas as pd
## package
from leylab_pipelines import Utils

# logging
logging.basicConfig(
    level=logging.DEBUG, format='%(asctime)s|%(levelname)s|%(message)s')

# functions
def get_desc():
    desc = 'Convert an NCBI taxonomy (taxdump) into lineages (taxID<-->lineage)'
    return desc

def parse_args(test_args=None, subparsers=None):
    # desc
    desc = get_desc()
    epi = """DESCRIPTION:
    Convert the NCBI taxonomy dump to a lineage table.
    The lineage table can be used to map taxonomy IDs to taxonomy.

    If the specific taxonomy dump files are not provided, this command
    will download the full dump file, uncompress it, and use the needed
    files. By default, the dump files are downloaded to a temporary directory.
    You can keep the taxonomy dump by using the --outdir option.

    TO CONVERT ACCESSION TO TAX_ID:
      see ftp://ftp.ncbi.nih.gov/pub/taxonomy/accession2taxid/nucl_gb.accession2taxid.gz
    """
    if subparsers:
        parser = subparsers.add_parser('taxID2lintbl', description=desc, epilog=epi,
                                       formatter_class=argparse.RawTextHelpFormatter)
    else:
        parser = argparse.ArgumentParser(description=desc, epilog=epi,
                                         formatter_class=argparse.RawTextHelpFormatter)

    # args
    io = parser.add_argument_group('File input/output')
    io.add_argument('--nodes', default=None,
                    help='nodes.dmp file path. (default: %(default)s)')
    io.add_argument('--names', default=None,
                    help='names.dmp file path. (default: %(default)s)')
    io.add_argument('-o', '--outfile', default='NCBI_taxID2lin.txt',
                    help='Output file for lineage table (default: %(default)s)')

    dmp = parser.add_argument_group('Taxonomy dump')
    dmp.add_argument('-u', '--url', default='ftp://ftp.ncbi.nih.gov/pub/taxonomy/taxdump.tar.gz',
                    help='URL for downloading taxonomy dump. (default: %(default)s)')
    dmp.add_argument('-d', '--outdir', default=None,
                     help='Output directory for the taxonomy dump download. (default: %(default)s)')
    dmp.add_argument('-a', '--all', action='store_true', default=False,
                     help='Output all taxonomy levels instead of just the standard levels. (default: %(default)s)')

    misc = parser.add_argument_group('Misc')
    misc.add_argument('-p', '--procs', default=1,
                      help='Number of processors to use. (default: %(default)s)')

    # running test args
    if test_args:
        args = parser.parse_args(test_args)
        return args

def strip(str_):
    '''
    :param str_: a string
    '''
    return str_.strip()

def load_nodes(nodes_file):
    '''
    load nodes.dmp and convert it into a pandas.DataFrame
    '''
    df = pd.read_csv(nodes_file, sep='|', header=None, index_col=False,
                     names=[
                         'tax_id',
                         'parent_tax_id',
                         'rank',
                         'embl_code',
                         'division_id',
                         'inherited_div_flag',
                         'genetic_code_id',
                         'inherited_GC__flag',
                         'mitochondrial_genetic_code_id',
                         'inherited_MGC_flag',
                         'GenBank_hidden_flag',
                         'hidden_subtree_root_flag',
                         'comments'
                     ])

    # To get rid of flanking tab characters
    df['rank'] = df['rank'].apply(strip)
    df['embl_code'] = df['embl_code'].apply(strip)
    df['comments'] = df['comments'].apply(strip)
    return df

def load_names(names_file):
    '''
    load names.dmp and convert it into a pandas.DataFrame
    '''
    df = pd.read_csv(names_file, sep='|', header=None, index_col=False,
                     names=[
                         'tax_id',
                         'name_txt',
                         'unique_name',
                         'name_class'
                     ])
    df['name_txt'] = df['name_txt'].apply(strip)
    df['unique_name'] = df['unique_name'].apply(strip)
    df['name_class'] = df['name_class'].apply(strip)

    sci_df = df[df['name_class'] == 'scientific name']
    sci_df.reset_index(drop=True, inplace=True)
    return sci_df

def to_dict(lineage):
    """
    convert the lineage into a list of tuples in the form of

    [
        (tax_id1, rank1, name_txt1),
        (tax_id2, rank2, name_txt2),
        ...
    ]

    to a dict
    """
    dd = {}
    num_re = re.compile('[0-9]+')
    len_lineage = len(lineage)
    for k, __ in enumerate(lineage):
        tax_id, rank, name_txt = __
        # use the last rank as the tax_id, whatever it is, genus or species.
        if k == len_lineage - 1:
            dd['tax_id'] = tax_id

        # e.g. there could be multiple 'no rank'
        numbered_rank = rank
        while numbered_rank in dd:
            # print __, numbered_rank
            search = num_re.search(numbered_rank)
            if search is None:
                count = 1
            else:
                count = int(search.group()) + 1
            numbered_rank = '{0}{1}'.format(rank, count)
        dd[numbered_rank] = name_txt
    return dd

def find_lineage(tax_id):
    if tax_id % 50000 == 0:
        logging.debug('working on tax_id: {0}'.format(tax_id))
    lineage = []
    while True:
        rec = TAXONOMY_DICT[tax_id]
        lineage.append((rec['tax_id'], rec['rank'], rec['name_txt']))
        tax_id = rec['parent_tax_id']

        if tax_id == 1:
            break

    # reverse results in lineage of Kingdom => species, this is helpful for
    # to_dict when there are multiple "no rank"s
    lineage.reverse()
    return to_dict(lineage)

def get_taxdump(url, outDir=None):
    """Getting taxdump file from NCBI.
    Saving to a temporary directory.
    """
    logging.info('downloading NCBI taxonomy dump...')

    # downloading
    if outDir:
        # saving taxdump
        dmpFile = os.path.join(outDir, 'taxdump.tar.gz')
    else:
        # taxdump written to temporary directory
        outDir = tempfile.gettempdir()
        dmpFile = None
    if sys.version_info[0] >= 3:
        dmpFile, headers = urllib.request.urlretrieve(url, filename=dmpFile)
    else:
        dmpFile, headers = urllib.urlretrieve(url, filename=dmpFile)

    # uncompressing
    logging.info('uncompressing NCBI taxonomy dump file...')
    tar = tarfile.open(dmpFile, "r:gz")
    tar.extractall(path=outDir)
    tar.close()
    logging.info('files uncompressed to: {}'.format(outDir))

    # target file names
    files = {'nodes' : os.path.join(outDir, 'nodes.dmp'),
             'names' : os.path.join(outDir, 'names.dmp')}
    ## checking for existence
    for k,v in files.items():
        if not os.path.isfile(v):
            raise ValueError('Cannot find file: {}'.format(v))
    # ret
    return files

def main(args=None):
    # Input
    if args is None:
        args = parse_args()

    # data downloaded from ftp://ftp.ncbi.nih.gov/pub/taxonomy/
    if args.outdir is not None or args.nodes is None or args.names is None:
        files = get_taxdump(args.url, args.outdir)
        args.nodes = files['nodes']
        args.names = files['names']

    nodes_df = load_nodes(args.nodes)
    names_df = load_names(args.names)
    df = nodes_df.merge(names_df, on='tax_id')
    df = df[['tax_id', 'parent_tax_id', 'rank', 'name_txt']]
    df.reset_index(drop=True, inplace=True)
    logging.info('# of tax ids: {0}'.format(df.shape[0]))
    # log summary info about the dataframe
    df.info()

    # force to use global variable TAXONOMY_DICT because map doesn't allow
    # passing extra args easily
    global TAXONOMY_DICT
    logging.info('generating TAXONOMY_DICT...')
    TAXONOMY_DICT = dict(zip(df.tax_id.values, df.to_dict('records')))

    ncpus = int(args.procs)
    logging.info('using {0} cpus to find lineages for all tax ids'.format(ncpus))
    pool = multiprocessing.Pool(ncpus)
    lineages_dd = pool.map(find_lineage, df.tax_id.values)
    pool.close()

    logging.info('generating a dictionary of lineages information...')
    dd_for_df = dict(zip(range(len(lineages_dd)), lineages_dd))

    logging.info('generating lineages_df...')
    lineages_df = pd.DataFrame.from_dict(dd_for_df, orient='index')
    lineages_df.sort_values('tax_id', inplace=True)

    logging.info('writing lineages to: {}'.format(args.outfile))
    cols = ['tax_id',
            'superkingdom',
            'phylum',
            'class',
            'order',
            'family',
            'genus',
            'species']

    other_cols = sorted([__ for __ in lineages_df.columns if __ not in cols])
    if args.all:
        output_cols = cols + other_cols
    else:
        output_cols = cols
    lineages_df.to_csv(args.outfile, index=False, columns=output_cols, sep='\t')


import pandas as pd

def MortgageAmortizationSchedule(
    loan_amount, annual_interest_rate, loan_term_months, monthly_prepayment
):
    monthly_interest_rate = annual_interest_rate / 12 / 100
    monthly_payment = (
        loan_amount
        * (monthly_interest_rate * (1 + monthly_interest_rate) ** loan_term_months)
        / ((1 + monthly_interest_rate) ** loan_term_months - 1)
    )

    schedule = []
    remaining_balance = loan_amount
    original_loan_amount = loan_amount
    original_loan_term_months = loan_term_months

    for month in range(1, loan_term_months + 1):
        interest_payment = remaining_balance * monthly_interest_rate
        principal_payment = monthly_payment - interest_payment

        # Check if the current month is within the first 2 years of loan
        if month <= 2 * 12:
            remaining_balance -= monthly_payment + monthly_prepayment - interest_payment
        else:
            remaining_balance -= monthly_payment - interest_payment

        if remaining_balance <= 0:
            # Loan is fully paid, no need to continue calculating the schedule
            loan_term_months = month
            break

        schedule.append(
            {
                "Month": month,
                "Monthly Payment": monthly_payment,
                "Interest Payment": interest_payment,
                "Principal Payment": principal_payment,
                "Prepayment": monthly_prepayment if month <= 2 * 12 else 0,
                "Remaining Balance": max(
                    0, remaining_balance
                ),  # Ensure balance doesn't go negative
            }
        )

    # Calculate total interest savings and total tenure reduced
    original_interest = (
        monthly_payment * original_loan_term_months
    ) - original_loan_amount
    new_interest = (monthly_payment * loan_term_months) - loan_amount
    interest_savings = original_interest - new_interest
    tenure_reduced_months = original_loan_term_months - loan_term_months
    total_interest = (monthly_payment * loan_term_months) - loan_amount
    total_principal = loan_amount

    summary = {
        "Total Interest Savings": interest_savings,
        "Total Tenure Reduced (months)": tenure_reduced_months,
        "Total interest": total_interest,
        "Total principal": total_principal,
    }

    return schedule, summary


# Done in 5/7/2018 12:50 AM. this one is pretty direct.
name = input("what is your name?")
weight_kg = float(input("what is your weight (KG):"))
height_m = float(input("what is your height (m):"))
bmi = weight_kg / (height_m ** 2)
print("bmi:",round(bmi,2))
if bmi < 18:
    print(name, "is underweight")
elif bmi < 25:
    print(name,"is normal")
elif bmi > 25.0 < 29.9:
    print(name,"is overweight")
elif bmi > 30.0:
    print(name,"is obese")


"""
In this challenge, you have to establish if a given integer is an Astonishing number. An Astonishing number is an integer that, when partitioned into two parts a (left) and b (right), is equal to the sum of the consecutive numbers from a up to b (if a is lower than b), or from b up to a (if a is greater than b).

Given a positive integer num, implement a function that returns:

The string "AB-Astonishing" if num is an Astonishing number and the partition a is lower than b.
The string "BA-Astonishing" if num is an Astonishing number and the partition a is greater than b.
False if num is not an Astonishing number.
Examples
is_astonishing(15) ➞ "AB-Astonishing"
# There is only one possible partition: a = 1 and b = 5
# Sum from a up to b: 1 + 2 + 3 + 4 + 5 = 15
# It's Astonishing and partition a is lower than partition b

is_astonishing(4020) ➞ False
# There are three possible partitions
# Partition 1: a = 4 and b = 020 = 20 (leading zeros are not considered)
# Sum from a up to b: 4 + 5 + 6 + ... + 20 = 204
# Partition 2: a = 40 and b = 20
# Sum from b up to a: 20 + 21 + 22 + ... + 40 = 630
#Partition 3: a = 402 and b = 0
# Sum from b to a: 0 + 1 + 2 + ... + 402 = 81003
# It's not Astonishing

is_astonishing(2002077) ➞ "BA-Astonishing"
# There are six possible partitions
# Partition 1: a = 2 and b = 002077 = 2077 (leading zeros are not considered)
# Sum from a up to b: 2 + 3 + 4 + ... + 2077 = 2158002
# Partition 2: a = 20 and b = 02077 = 2077
# Sum from a up to b: 20 + 21 + 22 + ... + 2077 = 2157813
# Partition 3: a = 200 and b = 2077
# Sum from a up to b: 200 + 201 + 202 + ... + 2077 = 2138103
# Partition 4: a = 2002 and b = 077 = 77
# Sum from b up to a: 77 + 78 + 79 + ... + 2002 = 2002077
# It's Astonishing and partition a is greater than partition b
Notes
Leading zeros in the b partition are not considered (see examples #2 and #3).
A valid partition has at least a number into it, and this number can be also 0 (see example #2).
You can expect positive integers greater than 9 as input (a single-digit number can't be partitioned).
"""

def get_sum(a, b):

    return b * (b + 1) // 2 - a * (a - 1) // 2

def is_astonishing(num):

    n = str(num)
    for i in range(1, len(n)):
        a, b = int(n[:i]), int(n[i:])
        if a < b and num == get_sum(a, b):
            return "AB-Astonishing"
        if b < a and num == get_sum(b, a):
            return "BA-Astonishing"
    return False

    # if count < len(str(num)):
    #     # if int(str(num)[count + 1:]):
    #
    #     a = int(str(num)[0:count + 1])
    #     b = int(str(num)[count + 1:])
    #
    #     # else:
    #     #     return False
    #
    # if a < b:
    #     total = int((a + b) * (b - a + 1) / 2)
    #     print(total)
    # else:
    #     return "BA-Astonishing"
    #
    # return is_astonishing(num, count+1)

# a = int(str(num)[0:count+1])
        # b = int(str(num)[count+1:])

    #     print(a, b)
    #

print(is_astonishing(15))
print(is_astonishing(2002077))
print(is_astonishing(190))
print(is_astonishing(23136928519))


import sys
import time
import requests
from urllib.error import URLError
from json.decoder import JSONDecodeError
from http.client import RemoteDisconnected
from selenium import webdriver
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By
from selenium.common.exceptions import TimeoutException, NoSuchElementException
from django.conf import settings
from tecqii.models import User

class Crawler():

    def __init__(self):
        self.driver = webdriver.PhantomJS(settings.PHANTOMJS_PATH)  # VPS用
        self.driver.set_window_size(1400, 1000)
        self.driver.implicitly_wait(10)

    def tear_down(self):
        self.driver.close()

    def crawl_qiita_user_ranking(self, page):
        QIITA_USER_RANKING_URL = 'https://qiita-user-ranking.herokuapp.com/'
        try:
            print('page: ',page,' --------------')
            self.driver.get(QIITA_USER_RANKING_URL+'?page='+str(page))
            element = WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.XPATH, '/html/body/main/h2')))
            user_ids = self.driver.find_elements_by_xpath('/html/body/main/p[4<position()<24]/a')
            for user_id in user_ids:
                print(user_id.text.strip())
                User.objects.update_or_create(
                    user_id=user_id.text.strip(),
                )
        except TimeoutException:
            print('loading took too much time')
            return False

    # calls the internal api
    def crawl_qiita_users(self, char='A', page=1):
        QIITA_USERS_URL = 'https://qiita.com/users'
        QIITA_INTERNALAPI_HOVERCARD = 'https://qiita.com/api/internal/hovercard_users/'
        try:
            print('char: ', char, 'page: ', page)
            self.driver.get(QIITA_USERS_URL + '?char=' + char + '&page=' + str(page))
            WebDriverWait(self.driver, 30).until(EC.presence_of_element_located((By.XPATH, '//*[@id="main"]/div/div/div[1]/h2')))
        except TimeoutException:
            print('loading took too much time')
        else:
            user_ids = self.driver.find_elements_by_xpath('//*[@id="main"]/div/div/div[2]/div[*]/div/div/p[1]/a')
            for user_id in user_ids:
                try:
                    hover_card = requests.request('GET', QIITA_INTERNALAPI_HOVERCARD+user_id.text.strip()) # using internal api.
                    hover_card_json = hover_card.json()
                    user = User.objects.update_or_create(
                        user_id=user_id.text.strip(),
                        defaults={
                            'items_count': hover_card_json['articles_count'],
                            'contribution_count': int(hover_card_json['contribution']),
                            'permanent_id': int(hover_card_json['id']),
                            'name': hover_card_json['name'],
                            'profile_image_url': hover_card_json['profile_image_url']
                        }
                    )
                except JSONDecodeError:
                    user = User.objects.update_or_create(
                        user_id=user_id.text.strip(),
                    )
                    print(user, ' JSONDecodeError')
                except RemoteDisconnected:
                    print('RemoteDisconnected. will wait 1 minute.')
                    break
                except URLError:
                    print('URLError. will wait 1 minute.')
                    break
            try:
                self.driver.find_element_by_xpath('//*[@id="main"]/div/div/div[2]/div[101]/ul/li[2]/a')
            except NoSuchElementException:
                print('no next button, will switch to next char.')
                return False
            except:
                print(sys.exc_info()[1])
            else:
                return True

    # not calling the internal api
    def crawl_qiita_users_light(self, char='A', page=1):
        QIITA_USERS_URL = 'https://qiita.com/users'
        try:
            print('char: ', char, 'page: ', page)
            self.driver.get(QIITA_USERS_URL + '?char=' + char + '&page=' + str(page))
            WebDriverWait(self.driver, 30).until(EC.presence_of_element_located((By.XPATH, '//*[@id="main"]/div/div/div[1]/h2')))
        except TimeoutException:
            print('loading took too much time')
        else:
            time.sleep(1)
            user_ids = self.driver.find_elements_by_xpath('//*[@id="main"]/div/div/div[2]/div[*]/div/div/p[1]/a')
            for user_id in user_ids:
                user = User.objects.update_or_create(
                    user_id=user_id.text.strip()
                )
            try:
                self.driver.find_element_by_xpath('//*[@id="main"]/div/div/div[2]/div[101]/ul/li[2]/a')
            except NoSuchElementException:
                print('no next button, will switch to next char.')
                return False
            except:
                print(sys.exc_info()[1])
            else:
                return True

    def crawl_qiita_user(self, user_id):
        QIITA_URL = 'https://qiita.com/'
        try:
            print('user_id: ',user_id)
            self.driver.get(QIITA_URL + user_id)
            WebDriverWait(self.driver, 10).until(EC.presence_of_element_located((By.CSS_SELECTOR,'#main > div > div > div.col-md-3.col-sm-3.col-xs-12.newUserPageProfile > div.newUserPageProfile_avatar > img')))
        except TimeoutException:
            print('loading took too much time')
        except NoSuchElementException: # Consider the user no longer exists.
            deleted_user = User.objects.get(user_id=user_id)
            deleted_user.delete()
        else:
            try:
                description = self.driver.find_element_by_class_name('newUserPageProfile_description')
                description = description.text
            except NoSuchElementException:
                description = ''
            try:
                facebook_id = self.driver.find_element_by_css_selector(
                    '#main > div > div > div.col-md-3.col-sm-3.col-xs-12.newUserPageProfile > div.newUserPageProfile_header > ul > li.newUserPageProfile_socialLink-facebook > a')
                facebook_id = facebook_id.get_attribute('href')
                facebook_id = facebook_id.replace('https://facebook.com/', '')
            except NoSuchElementException:
                facebook_id = ''
            try:
                followees_count = self.driver.find_elements_by_class_name('userActivityChart_statCount')[2]
                followees_count = int(followees_count.text)
            except NoSuchElementException:
                followees_count = 0
            try:
                followers_count = self.driver.find_element_by_css_selector('#main > div > div > div.col-md-3.col-sm-3.col-xs-12.newUserPageProfile > div:nth-child(6) > div.newUserPageProfile_info_heading > span > a')
                followers_count = followers_count.text
            except NoSuchElementException:
                followers_count = 0
            try:
                github_login_name = self.driver.find_element_by_css_selector('#main > div > div > div.col-md-3.col-sm-3.col-xs-12.newUserPageProfile > div.newUserPageProfile_header > ul > li.newUserPageProfile_socialLink-github > a')
                github_login_name = github_login_name.get_attribute('href')
                github_login_name = github_login_name.replace('https://github.com/', '')
            except NoSuchElementException:
                github_login_name = ''
            try:
                items_count = self.driver.find_elements_by_class_name('userActivityChart_statCount')[0]
                items_count = int(items_count.text)
            except NoSuchElementException:
                items_count = 0
            try:
                linkedin_id = self.driver.find_element_by_css_selector('#main > div > div > div.col-md-3.col-sm-3.col-xs-12.newUserPageProfile > div.newUserPageProfile_header > ul > li.newUserPageProfile_socialLink-linkedin > a')
                linkedin_id = linkedin_id.get_attribute('href')
                linkedin_id = linkedin_id.replace('https://www.linkedin.com/in/','')
            except NoSuchElementException:
                linkedin_id = ''
            try:
                location = self.driver.find_element_by_css_selector('#main > div > div > div.col-md-3.col-sm-3.col-xs-12.newUserPageProfile > div:nth-child(3) > div:nth-child(4)')
                location = location.text
            except NoSuchElementException:
                location = ''
            try:
                name = self.driver.find_element_by_class_name('newUserPageProfile_fullName')
                name = name.text
            except NoSuchElementException:
                name = ''
            try:
                organization = self.driver.find_element_by_css_selector('#main > div > div > div.col-md-3.col-sm-3.col-xs-12.newUserPageProfile > div:nth-child(3) > div:nth-child(3)')
                organization = organization.text
            except NoSuchElementException:
                organization = ''
            try:
                profile_image_url = self.driver.find_element_by_css_selector('#main > div > div > div.col-md-3.col-sm-3.col-xs-12.newUserPageProfile > div.newUserPageProfile_avatar > img')
                profile_image_url = profile_image_url.get_attribute('src')
            except NoSuchElementException:
                profile_image_url = ''
            try:
                twitter_screen_name = self.driver.find_element_by_css_selector('#main > div > div > div.col-md-3.col-sm-3.col-xs-12.newUserPageProfile > div.newUserPageProfile_header > ul > li.newUserPageProfile_socialLink-twitter > a')
                twitter_screen_name = twitter_screen_name.get_attribute('href')
                twitter_screen_name = twitter_screen_name.replace('https://twitter.com/','')
            except NoSuchElementException:
                twitter_screen_name = ''
            try:
                website_url = self.driver.find_element_by_css_selector('#main > div > div > div.col-md-3.col-sm-3.col-xs-12.newUserPageProfile > div:nth-child(3) > div:nth-child(2) > a')
                website_url = website_url.get_attribute('href')
            except NoSuchElementException:
                website_url = ''
            try:
                contribution_count = self.driver.find_elements_by_class_name('userActivityChart_statCount')[1]
                contribution_count = contribution_count.text
            except NoSuchElementException:
                contribution_count = 0

            User.objects.update_or_create(
                user_id=user_id,
                defaults={
                    'description': description,
                    'facebook_id': facebook_id,
                    'followees_count': followees_count,
                    'followers_count': followers_count,
                    'github_login_name': github_login_name,
                    'items_count': items_count,
                    'linkedin_id': linkedin_id,
                    'location': location,
                    'name': name,
                    'organization': organization,
                    'profile_image_url': profile_image_url,
                    'twitter_screen_name': twitter_screen_name,
                    'website_url': website_url,
                    'contribution_count': contribution_count,
                }
            )


#!/usr/bin/env python3
import click
import trimesh
import numpy as np

def mesh2arrayCentered(mesh, voxel_size=1, array_length=32):
    # given array length 64, voxel size 2, then output array size is [128,128,128]
    array_size = np.ceil(
        np.array([array_length, array_length, array_length]) / voxel_size
    ).astype(int)
    vox_array = np.zeros(
        array_size, dtype=bool
    )  # tanh: voxel representation [-1,1], sigmoid: [0,1]
    # scale mesh extent to fit array_length
    max_length = np.max(np.array(mesh.extents))
    mesh = mesh.apply_transform(
        trimesh.transformations.scale_matrix((array_length - 1) / max_length)
    )  # now the extent is [array_length**3]
    v = mesh.voxelized(voxel_size)  # max voxel array length = array_length / voxel_size

    # find indices in the v.matrix to center it in vox_array
    indices = ((array_size - v.matrix.shape) / 2).astype(int)
    vox_array[
        indices[0] : indices[0] + v.matrix.shape[0],
        indices[1] : indices[1] + v.matrix.shape[1],
        indices[2] : indices[2] + v.matrix.shape[2],
    ] = v.matrix

    return vox_array

@click.command()
@click.option("--input", "-i", required=True)
@click.option("--task", "-t", type=click.Choice(["check", "voxelize"]))
def cli(input, task):
    print(f"Loading {input}")
    mesh = trimesh.load(input, force="mesh")
    if task == "voxelize":
        array = mesh2arrayCentered(mesh)
        voxelmesh = trimesh.voxel.VoxelGrid(
            trimesh.voxel.encoding.DenseEncoding(array)
        ).marching_cubes
        # voxelmesh.show()
        print("ok")

main = cli

if __name__ == "__main__":
    cli()


import json

# 节点列表，存放病例信息
list_nodes = []
# 边列表，存放传染关系
list_edges = []
# 输出json文件的字典
outdic = {
    "nodes": list_nodes,
    "edges": list_edges
}

def add_a_node(pid, phone, name, gender, diagnosedTime, level, type):
    tmp_dic = {
        "pid": pid,
        "phone": phone,
        "name": name,
        "gender": gender,
        "diagnosedTime": diagnosedTime,
        "level": level,
        "type": type
    }
    list_nodes.append(tmp_dic)

def add_a_edge(id, source, target, relation, isTruth):
    tmp_dic = {
        "id": id,
        "source": source,
        "target": target,
        "relation": relation,
        "isTruth": isTruth
    }
    list_edges.append(tmp_dic)

if __name__ == '__main__':
    with open("reason_result.json", 'r', encoding='utf-8') as fw:
        reason_result = json.load(fw)

    edge_num = 0
    for i in range(len(reason_result)):
        if reason_result[i].get('recorded') == 0:
            list_nodes.append(reason_result[i])
            continue
        pid = reason_result[i].get('pid')
        phone = "12345678901"                # search_database
        name = reason_result[i].get('name')
        gender = "男"                        # search_database
        diagnosedTime = "2021-11-13"         # search_database
        if reason_result[i].get('father') != -1:
            level = str(int(list_nodes[reason_result[i].get('father')].get('level')) + 1)
        else:
            level = "0"
        typee = "确诊"                        # search_database

        add_a_node(pid, phone, name, gender, diagnosedTime, level, typee)

        if reason_result[i].get('father') != -1:
            id = str(edge_num)
            source = str(reason_result[i].get('father'))
            target = str(i)
            relation = reason_result[i].get('relation')
            isTruth = reason_result[i].get('isTruth')

            edge_num += 1
            add_a_edge(id, source, target, relation, isTruth)

    with open("test.json", 'w', encoding='utf-8') as fw:
        json.dump(outdic, fw, indent=4, ensure_ascii=False)


import os
import sys
from functools import partial

# >>>>>>  Package Imports <<<<<<<
import numpy as np

# >>>>>>  Local Imports   <<<<<<<
from barl import BarlException
from .interfaces import StateLessEnvironment

####################################################
# CODE
####################################################

class MultiArmedBandit(StateLessEnvironment):
    """
    MultiArmedbandit - MAB
    """
    #Allowed Distributions
    distributionMapping = {
        "Normal" : np.random.normal,
    }

    def __init__(self, arms=None, means=None, variances=None, distribution="Normal"):
        if arms:
            self.arms = arms
            if not means:
                means = MultiArmedBandit.get_means( arms )

            if not variances:
                variances = MultiArmedBandit.get_variances( arms )
        else:
            if not means:
                raise BarlException("Need to provide Means to Multiarmed Bandit")
            else:
                self.arms = len(means)

            if not variances:
                variances = MultiArmedBandit.get_variances( self.arms )

        self.__means = means
        #to allow single number input
        flag = type(variances)==np.ndarray or type(variances)==list
        self.__variances = variances if flag else np.ones(self.arms) * variances

        self.__distribution = distribution

        self.__set_arms()

    def sample_rewards(self, action=None, N=1):
        """
        samples rewards: np.array ( size[__arms,N] )
        """
        if type(action)==type(None):

            outList = []

            for arm in self.__arms:

                outList.append( arm(N) )

            rewards = np.array( outList )

        else:

            rewards = self.__arms[ int(action) ](N)

        return np.squeeze( rewards )

    def get_true_variances(self):
        """
        """
        return self.__variances

    def get_true_means(self):
        """
        """
        return self.__means

    def __set_arms(self):
        """
        sets the arm properties
        """

        self.__arms = []

        distro = MultiArmedBandit.distributionMapping[self.__distribution]

        for mean, variance in zip(self.__means,self.__variances):

            temp = partial(distro, mean, variance)

            self.__arms.append(temp)

    @staticmethod
    def get_variances(arms = 2):
        """
        returns [v1,v2, ...v___arms] sampled from ~Unif(0,1)
        """
        return np.random.beta(a=1,b=1,size=[arms])

    @staticmethod
    def get_means(arms = 2):
        """
        returns [m1,m2, ...m___arms] sampled from ~Normal(0,1)
        """
        return np.random.normal(loc=0.0, scale=1.0, size=[arms])

####################################################
# MAIN
####################################################

# EOF


# Solved:
# (E) Search Insert Position
# https://leetcode.com/problems/search-insert-position/
# BINS: Search for leftmost

from typing import List

class Solution:
    def searchInsert(self, nums: List[int], target: int) -> int:
        # We should find leftmost 'target' value in the list of nums
        l = 0
        r = len(nums) - 1
        while r - l > 1:
            m = (l + r) // 2
            if target <= nums[m]:
                r = m
            else:
                l = m + 1
        if nums[r] < target:
            return r + 1
        elif nums[l] < target:
            return l + 1
        else:
            return l

x = Solution()

nums = [1]

for i in range(0, 11):
    print(f"{i} -> {x.searchInsert(nums, i)}")