# Author: Nicolas Legrand <nicolas.legrand@cfin.au.dk>

import sys
import warnings
from math import sqrt
from typing import Callable, Dict, List, Optional, Union, overload

import numpy as np
import pandas as pd
import pandas_flavor as pf
import scipy.special.cython_special as cysp
from numba import jit, vectorize
from numba.extending import get_cython_function_address
from numba.types import WrapperAddressProtocol, float32, float64
from scipy.optimize import SR1, Bounds, LinearConstraint, minimize
from scipy.stats import norm
from tqdm import tqdm

from metadpy.utils import trials2counts

warnings.filterwarnings(
    "ignore", message="delta_grad == 0.0. Check if the approximated function is linear."
)

_signatures = [
    float32(float32, float32, float32),
    float64(float64, float64, float64),
]

def get(name, signature):
    """Get Scipy Cython function."""
    index = 1 if signature.return_type is float64 else 0
    pyx_fuse_name = f"__pyx_fuse_{index}{name}"
    if pyx_fuse_name in cysp.__pyx_capi__:
        name = pyx_fuse_name
    addr = get_cython_function_address("scipy.special.cython_special", name)

    cls = type(
        name,
        (WrapperAddressProtocol,),
        {"__wrapper_address__": lambda self: addr, "signature": lambda self: signature},
    )
    return cls()

erf = get("erf", float64(float64))

@vectorize(_signatures)
def norm_cdf(
    x: Union[float, np.ndarray],
    mu: Union[float, np.ndarray],
    sigma: Union[float, np.ndarray],
):
    """Evaluate cumulative distribution function of normal distribution."""
    z = (x - mu) / sigma
    z *= 1.0 / sqrt(2)
    return 0.5 * (1.0 + erf(z))

@jit(nopython=True)
def fit_meta_d_logL(
    guess: np.ndarray,
    nR_S1: np.ndarray,
    nR_S2: np.ndarray,
    nRatings: int,
    d1: float,
    t1c1: float,
    s: int,
) -> float:
    """Negative log-likelihood of parameters given experimental data.

    Parameters
    ----------
    guess :
        guess[0] = meta d'
        guess[1:end] = type-2 criteria locations
    nR_S1, nR_S2 :
        These are vectors containing the total number of responses in each response
        category, conditional on presentation of S1 and S2.
    nRatings :
        Numbers of ratings.
    d1 :
        d prime.
    t1c1 :
        The type-1 criterion.
    s :
        Ratio of standard deviations for type 1 distributions as:
        `s = np.std(S1) / np.std(S2)`. If not specified, s is set to a default
        value of 1. For most purposes, it is recommended to set `s=1`. See
        http://www.columbia.edu/~bsm2105/type2sdt for further discussion.

    """
    meta_d1, t2c1 = guess[0], guess[1:]

    # define mean and SD of S1 and S2 distributions
    S1mu = -meta_d1 / 2
    S1sd = 1
    S2mu = meta_d1 / 2
    S2sd = S1sd / s

    # adjust so that the type 1 criterion is set at 0
    # (this is just to work with optimization toolbox constraints...
    #  to simplify defining the upper and lower bounds of type 2 criteria)
    S1mu = S1mu - (meta_d1 * (t1c1 / d1))
    S2mu = S2mu - (meta_d1 * (t1c1 / d1))

    t1c1 = 0

    # set up MLE analysis
    # get type 2 response counts
    # S1 responses
    nC_rS1, nI_rS1 = [], []
    for i in range(nRatings):
        nC_rS1.append(nR_S1[i])
        nI_rS1.append(nR_S2[i])

    # S2 responses
    nC_rS2 = [nR_S2[i + nRatings] for i in np.arange(nRatings)]
    nI_rS2 = [nR_S1[i + nRatings] for i in np.arange(nRatings)]

    # get type 2 probabilities
    C_area_rS1 = norm_cdf(t1c1, S1mu, S1sd)
    I_area_rS1 = norm_cdf(t1c1, S2mu, S2sd)

    C_area_rS2 = 1 - norm_cdf(t1c1, S2mu, S2sd)
    I_area_rS2 = 1 - norm_cdf(t1c1, S1mu, S1sd)

    t2c1x = [-np.inf]
    t2c1x.extend(t2c1[0 : (nRatings - 1)])
    t2c1x.append(t1c1)
    t2c1x.extend(t2c1[(nRatings - 1) :])
    t2c1x.append(np.inf)

    prC_rS1 = [
        (norm_cdf(t2c1x[i + 1], S1mu, S1sd) - norm_cdf(t2c1x[i], S1mu, S1sd))
        / C_area_rS1
        for i in range(nRatings)
    ]
    prI_rS1 = [
        (norm_cdf(t2c1x[i + 1], S2mu, S2sd) - norm_cdf(t2c1x[i], S2mu, S2sd))
        / I_area_rS1
        for i in range(nRatings)
    ]

    prC_rS2 = [
        (
            (1 - norm_cdf(t2c1x[nRatings + i], S2mu, S2sd))
            - (1 - norm_cdf(t2c1x[nRatings + i + 1], S2mu, S2sd))
        )
        / C_area_rS2
        for i in range(nRatings)
    ]
    prI_rS2 = [
        (
            (1 - norm_cdf(t2c1x[nRatings + i], S1mu, S1sd))
            - (1 - norm_cdf(t2c1x[nRatings + i + 1], S1mu, S1sd))
        )
        / I_area_rS2
        for i in range(nRatings)
    ]

    # calculate logL
    logL = 0.0
    for i in range(nRatings):
        logL = (
            logL
            + nC_rS1[i] * np.log(prC_rS1[i])
            + nI_rS1[i] * np.log(prI_rS1[i])
            + nC_rS2[i] * np.log(prC_rS2[i])
            + nI_rS2[i] * np.log(prI_rS2[i])
        )

    # returning -inf may cause optimize.minimize() to fail
    if np.isinf(logL) or np.isnan(logL):
        logL = -1e300

    return -logL

@overload
def metad(
    data: None,
    nRatings: int,
    nR_S1: None,
    nR_S2: None,
    stimuli: Union[list, np.ndarray],
    accuracy: Union[list, np.ndarray],
    confidence: Union[list, np.ndarray],
    within=None,
    between=None,
    subject=None,
    padAmount: Optional[float] = None,
    s: int = 1,
    padding: bool = True,
    collapse: Optional[int] = None,
    fncdf: Callable[..., float] = norm.cdf,
    fninv: Callable[..., float] = norm.ppf,
    verbose: int = 1,
) -> Union[dict, pd.DataFrame]:
    ...

@overload
def metad(
    data: None,
    nRatings: int,
    nR_S1: Union[list, np.ndarray],
    nR_S2: Union[list, np.ndarray],
    stimuli: None,
    accuracy: None,
    confidence: None,
    within=None,
    between=None,
    subject=None,
    padAmount: Optional[float] = None,
    s: int = 1,
    padding: bool = True,
    collapse: Optional[int] = None,
    fncdf: Callable[..., float] = norm.cdf,
    fninv: Callable[..., float] = norm.ppf,
    verbose: int = 1,
) -> pd.DataFrame:
    ...

@overload
def metad(
    data: pd.DataFrame,
    nRatings: int,
    nR_S1: None,
    nR_S2: None,
    stimuli: str = "Stimuli",
    accuracy: str = "Accuracy",
    confidence: str = "Confidence",
    within: Optional[str] = None,
    between: Optional[str] = None,
    subject: Optional[str] = None,
    padAmount: Optional[float] = None,
    s: int = 1,
    padding: bool = True,
    collapse: Optional[int] = None,
    fncdf: Callable[..., float] = norm.cdf,
    fninv: Callable[..., float] = norm.ppf,
    verbose: int = 1,
) -> pd.DataFrame:
    ...

@pf.register_dataframe_method
def metad(
    data=None,
    nRatings=4,
    stimuli="Stimuli",
    accuracy="Accuracy",
    confidence="Confidence",
    within=None,
    between=None,
    subject=None,
    padAmount=None,
    nR_S1=None,
    nR_S2=None,
    s=1,
    padding=True,
    collapse=None,
    fncdf=norm_cdf,
    fninv=norm.ppf,
    verbose=0,
):
    """Estimate meta-d' using maximum likelihood estimation (MLE).

    This function is adapted from the transcription of fit_meta_d_MLE.m
    (Maniscalco & Lau, 2012) by Alan Lee: http://www.columbia.edu/~bsm2105/type2sdt/.

    Parameters
    ----------
    data :
        Dataframe. Note that this function can also directly be used as a
        Pandas method, in which case this argument is no longer needed.
    nRatings :
        Number of discrete ratings. If a continuous rating scale was used, and
        the number of unique ratings does not match `nRatings`, will convert to
        discrete ratings using :py:func:`metadpy.utils.discreteRatings`.
        Default is set to 4.
    stimuli :
        Name of the column containing the stimuli.
    accuracy :
        Name of the columns containing the accuracy.
    confidence :
        Name of the column containing the confidence ratings.
    within :
        Name of column containing the within factor (condition comparison).
    between :
        Name of column containing the between subject factor (group
        comparison).
    subject :
        Name of column containing the subject identifier (only required if a
        within-subject or a between-subject factor is provided).
    nR_S1, nR_S2 :
        These are vectors containing the total number of responses in
        each response category, conditional on presentation of S1 and S2. If
        nR_S1 = [100, 50, 20, 10, 5, 1], then when stimulus S1 was presented, the
        subject had the following response counts:
        * responded `'S1'`, rating=`3` : 100 times
        * responded `'S1'`, rating=`2` : 50 times
        * responded `'S1'`, rating=`1` : 20 times
        * responded `'S2'`, rating=`1` : 10 times
        * responded `'S2'`, rating=`2` : 5 times
        * responded `'S2'`, rating=`3` : 1 time

        The ordering of response / rating counts for S2 should be the same as
        it is for S1. e.g. if nR_S2 = [3, 7, 8, 12, 27, 89], then when stimulus S2
        was presented, the subject had the following response counts:
        * responded `'S1'`, rating=`3` : 3 times
        * responded `'S1'`, rating=`2` : 7 times
        * responded `'S1'`, rating=`1` : 8 times
        * responded `'S2'`, rating=`1` : 12 times
        * responded `'S2'`, rating=`2` : 27 times
        * responded `'S2'`, rating=`3` : 89 times

    s :
        Ratio of standard deviations for type 1 distributions as:
        `s = np.std(S1) / np.std(S2)`. If not specified, s is set to a default
        value of 1. For most purposes, it is recommended to set `s=1`. See
        http://www.columbia.edu/~bsm2105/type2sdt for further discussion.
    padding :
        If `True`, a small value will be added to the counts to avoid problems
        during fit.
    padAmount :
        The value to add to each response count if padding is set to 1.
        Default value is 1/(2*nRatings)
    collapse :
        If an integer `N` is provided, will collpase ratings to avoid zeros by
        summing every `N` consecutive ratings. Default set to `None`.
    fncdf :
        A function handle for the CDF of the type 1 distribution. If not
        specified, fncdf defaults to :py:func:`scipy.stats.norm.cdf()`.
    fninv :
        A function handle for the inverse CDF of the type 1 distribution. If
        not specified, fninv defaults to :py:func:`scipy.stats.norm.ppf()`.
    verbose :
        Level of algorithm's verbosity (can be `0`, `1`, `2` or `3`):
        * 0 (default) : work silently.
        * 1 : display a termination report.
        * 2 : display progress during iterations.
        * 3 : display progress during iterations (more complete report).

    Returns
    -------
    results :
        In the following, S1 and S2 represent the distributions of evidence
        generated by stimulus classes S1 and S2:
        * `'dprime'` : `mean(S2) - mean(S1)`, in
            root-mean-square(sd(S1), sd(S2)) units
        * `'s'` : `sd(S1) / sd(S2)`
        * `'meta_da'` : meta-d' in RMS units
        * `'M_diff'` : `meta_da - da`
        * `'M_ratio'` : `meta_da / da`
        * `'meta_ca'` : type 1 criterion for meta-d' fit, RMS units.
        * `'t2ca_rS1'` : type 2 criteria of "S1" responses for meta-d' fit,
            RMS units.
        * `'t2ca_rS2'` : type 2 criteria of "S2" responses for meta-d' fit,
            RMS units.
        * `'logL'` : log likelihood of the data fit
        * `'est_HR2_rS1'` : estimated (from meta-d' fit) type 2 hit rates
            for S1 responses.
        * `'obs_HR2_rS1'` : actual type 2 hit rates for S1 responses.
        * `'est_FAR2_rS1'` : estimated type 2 false alarm rates for S1
            responses.
        * `'obs_FAR2_rS1'` : actual type 2 false alarm rates for S1
            responses.
        * `'est_HR2_rS2'` : estimated type 2 hit rates for S2 responses.
        * `'obs_HR2_rS2'` : actual type 2 hit rates for S2 responses.
        * `'est_FAR2_rS2'` : estimated type 2 false alarm rates for S2
            responses.
        * `'obs_FAR2_rS2'` : actual type 2 false alarm rates for S2
            responses.

    Notes
    -----
    Given data from an experiment where an observer discriminates between two
    stimulus alternatives on every trial and provides confidence ratings,
    provides a type 2 SDT analysis of the data.

    .. warning:: If nR_S1 or nR_S2 contain zeros, this may interfere with
        estimation of meta-d'. Some options for dealing with response cell
        counts containing zeros are:

        * Add a small adjustment factor (e.g. `1/(len(nR_S1)`, to each input
            vector. This is a generalization of the correction for similar
            estimation issues of type 1 d' as recommended in [1]_. When using
            this correction method, it is recommended to add the adjustment
            factor to ALL data for all subjects, even for those subjects whose
            data is not in need of such correction, in order to avoid biases in
            the analysis (cf [2]_). Use `padding==True` to activate this
            correction.

        * Collapse across rating categories. e.g. if your data set has 4
            possible confidence ratings such that `len(nR_S1)==8`, defining new
            input vectors:

            >>> nR_S1 = nR_S1.reshape(int(len(nR_S1)/collapse), 2).sum(axis=1)

            This might be sufficient to eliminate zeros from the input without
            using an adjustment. Use `collapse=True` to activate this
            correction.

    If there are N ratings, then there will be N-1 type 2 hit rates and false
    alarm rates.

    Examples
    --------
    No correction
    >>> nR_S1 = [36, 24, 17, 20, 10, 12, 9, 2]
    >>> nR_S2 = [1, 4, 10, 11, 19, 18, 28, 39]
    >>> fit = fit_meta_d_MLE(nR_S1, nR_S2, padding=False)

    Correction by padding values
    >>> nR_S1 = [36, 24, 17, 20, 10, 12, 9, 2]
    >>> nR_S2 = [1, 4, 10, 11, 19, 18, 28, 39]
    >>> fit = fit_meta_d_MLE(nR_S1, nR_S2, padding=True)

    Correction by collapsing values
    >>> nR_S1 = [36, 24, 17, 20, 10, 12, 9, 2]
    >>> nR_S2 = [1, 4, 10, 11, 19, 18, 28, 39]
    >>> fit = fit_meta_d_MLE(nR_S1, nR_S2, collapse=2)

    References
    ----------
    ..[1] Hautus, M. J. (1995). Corrections for extreme proportions and their
      biasing effects on estimated values of d'. Behavior Research Methods,
    Instruments, & Computers, 27, 46-51.

    ..[2] Snodgrass, J. G., & Corwin, J. (1988). Pragmatics of measuring
      recognition memory: Applications to dementia and amnesia. Journal of
      Experimental Psychology: General, 117(1), 34–50.
      https://doi.org/10.1037/0096-3445.117.1.34

    """
    if verbose:
        print("Estimate meta-d' using maximum likelihood estimation (MLE).")
    if isinstance(data, pd.DataFrame):
        if padAmount is None:
            padAmount = 1 / (2 * nRatings)

        if subject is None:
            subject = "Subject"
            data.loc[:, subject] = "Subject 1"
        if within is None:
            within = "Within"
            data.loc[:, within] = "Condition 1"
        if between is None:
            between = "Between"
            data.loc[:, between] = "Group 1"
    else:
        if (nR_S1 is not None) & (nR_S2 is not None):
            data = pd.DataFrame(
                {
                    "nR_S1": [nR_S1],
                    "nR_S2": [nR_S2],
                }
            )
        elif (stimuli is not None) & (accuracy is not None) & (confidence is not None):
            data = pd.DataFrame(
                {"Stimuli": stimuli, "Accuracy": accuracy, "Confidence": confidence}
            )
            stimuli, accuracy, confidence = "Stimuli", "Accuracy", "Confidence"
        subject, within, between = "Subject", "Within", "Between"
        data[subject] = "Subject 1"
        data[within] = "Condition 1"
        data[between] = "Group 1"

    if verbose:
        print(f"- n Subjects: {data[subject].nunique()}")
        print(f"- n Conditions: {data[within].nunique()}")
        print(f"- n Groups: {data[between].nunique()}")
    if (padding is True) & verbose:
        tqdm.write("... Using padding to avoid fitting errors.")
    if (collapse is True) & verbose:
        tqdm.write("... Collapse across rating categories.")

    results_df = pd.DataFrame([])

    if verbose:
        pbar = tqdm(data[subject].unique(), position=0, leave=True, file=sys.stdout)
    else:
        pbar = data[subject].unique()

    for sub in pbar:

        if verbose:
            pbar.set_description(f"Fitting model on subject: {sub}")

        for cond in data[within].unique():

            for group in data[between].unique():

                if (nR_S1 is None) & (nR_S2 is None):
                    this_df = data[
                        (data[between] == group)
                        & (data[within] == cond)
                        & (data[subject] == sub)
                    ]

                    nR_S1, nR_S2 = trials2counts(
                        data=this_df,
                        stimuli=stimuli,
                        accuracy=accuracy,
                        confidence=confidence,
                        nRatings=nRatings,
                        padding=padding,
                        padAmount=padAmount,
                    )

                if (len(nR_S1) % 2) != 0:
                    raise ValueError(
                        "input arrays must have an even number of elements"
                    )
                if len(nR_S1) != len(nR_S2):
                    raise ValueError(
                        "input arrays must have the same number of elements"
                    )

                if (padding is False) & (collapse is None):
                    if any(np.array(nR_S1) == 0) or any(np.array(nR_S2) == 0):
                        import warnings

                        warnings.warn(
                            (
                                "Your inputs contain zeros and is not corrected."
                                "This may interfere with proper estimation of meta-d."
                                "See docstrings for more information."
                            )
                        )
                elif (padding is True) & (collapse is None):
                    # A small padding is required to avoid problems in model fit if any
                    # confidence ratings aren't used (see Hautus MJ, 1995 for details)
                    if padAmount is None:
                        padAmount = 1 / len(nR_S1)
                    nR_S1 = nR_S1 + padAmount
                    nR_S2 = nR_S2 + padAmount
                elif (padding is False) & (collapse is not None):
                    # Collapse values accross ratings to avoid problems in model fit
                    nR_S1 = nR_S1.reshape(int(len(nR_S1) / collapse), 2).sum(axis=1)
                    nR_S2 = nR_S2.reshape(int(len(nR_S2) / collapse), 2).sum(axis=1)
                elif (padding is True) & (collapse is not None):
                    raise ValueError("Both padding and collapse are True.")

                if nRatings is None:
                    nRatings = int(
                        len(nR_S1) / 2
                    )  # number of ratings in the experiment
                nCriteria = int(2 * nRatings - 1)  # number criteria to be fitted

                try:
                    results_dict = fit_metad(
                        nR_S1=nR_S1,
                        nR_S2=nR_S2,
                        nRatings=nRatings,
                        nCriteria=nCriteria,
                        s=s,
                        verbose=verbose,
                        fninv=fninv,
                        fncdf=fncdf,
                    )
                    # Filter dictionary and convert into pd.DataFrame
                    results = pd.DataFrame(
                        {
                            k: [results_dict[k]]
                            for k in ["dprime", "meta_d", "m_ratio", "m_diff"]
                        }
                    )
                except ValueError:
                    import warnings

                    warnings.warn(
                        (
                            f"Error when fitting data for subject {str(sub)} - "
                            f"Condition {str(cond)} - Group {str(group)}."
                            "Returning NaNs as a result"
                        )
                    )
                    results = pd.DataFrame(
                        {
                            "dprime": [None],
                            "meta_d": [None],
                            "m_ratio": [None],
                            "m_diff": [None],
                        }
                    )

                if data[subject].nunique() > 1:
                    results[subject] = sub
                if data[within].nunique() > 1:
                    results[within] = cond
                if data[between].nunique() > 1:
                    results[between] = group

                results_df = pd.concat([results_df, results])

                nR_S1, nR_S2 = None, None

    return results_df

def fit_metad(
    nR_S1: np.ndarray,
    nR_S2: np.ndarray,
    nRatings: int,
    nCriteria: Optional[int] = None,
    s: int = 1,
    verbose: int = 0,
    fninv: Callable = norm.ppf,
    fncdf: Callable = norm_cdf,
) -> Dict:
    """Fit metad model using MLE.

    Parameters
    ----------
    nR_S1, nR_S2 :
        These are vectors containing the total number of responses in
        each response category, conditional on presentation of S1 and S2. If
        nR_S1 = [100, 50, 20, 10, 5, 1], then when stimulus S1 was presented, the
        subject had the following response counts:
        * responded `'S1'`, rating=`3` : 100 times
        * responded `'S1'`, rating=`2` : 50 times
        * responded `'S1'`, rating=`1` : 20 times
        * responded `'S2'`, rating=`1` : 10 times
        * responded `'S2'`, rating=`2` : 5 times
        * responded `'S2'`, rating=`3` : 1 time

        The ordering of response / rating counts for S2 should be the same as
        it is for S1. e.g. if nR_S2 = [3, 7, 8, 12, 27, 89], then when stimulus S2
        was presented, the subject had the following response counts:
        * responded `'S1'`, rating=`3` : 3 times
        * responded `'S1'`, rating=`2` : 7 times
        * responded `'S1'`, rating=`1` : 8 times
        * responded `'S2'`, rating=`1` : 12 times
        * responded `'S2'`, rating=`2` : 27 times
        * responded `'S2'`, rating=`3` : 89 times
    nRatings :
        Number of discrete ratings. If a continuous rating scale was used, and
        the number of unique ratings does not match `nRatings`, will convert to
        discrete ratings using :py:func:`metadpy.utils.discreteRatings`.
        Default is set to 4.
    nCriteria :
        (Optional) Number criteria to be fitted. If `None`, the number of criteria is
        set to `nCriteria = int(2 * nRatings - 1)`.
    s :
        Ratio of standard deviations for type 1 distributions as:
        `s = np.std(S1) / np.std(S2)`. If not specified, s is set to a default
        value of 1. For most purposes, it is recommended to set `s=1`. See
        http://www.columbia.edu/~bsm2105/type2sdt for further discussion.
    verbose :
        Level of algorithm's verbosity:
            * 0 (default) : work silently.
            * 1 : display a termination report.
            * 2 : display progress during iterations.
            * 3 : display progress during iterations (more complete report).
    fninv :
        A function handle for the inverse CDF of the type 1 distribution. If
        not specified, fninv defaults to :py:func:`scipy.stats.norm.ppf()`.
    fncdf :
        A function handle for the CDF of the type 1 distribution. If not
        specified, fncdf defaults to :py:func:`scipy.stats.norm.cdf()`.

    Returns
    -------
    results :
        In the following, S1 and S2 represent the distributions of evidence generated
        by stimulus classes S1 and S2:
        * `'dprime'` : `mean(S2) - mean(S1)`, in root-mean-square(sd(S1), sd(S2))
            units
        * `'s'` : `sd(S1) / sd(S2)`
        * `'meta_d'` : meta-d' in RMS units
        * `'m_diff'` : `meta_da - da`
        * `'m_ratio'` : `meta_da / da`
        * `'meta_ca'` : type 1 criterion for meta-d' fit, RMS units.
        * `'t2ca_rS1'` : type 2 criteria of "S1" responses for meta-d' fit, RMS
        units.
        * `'t2ca_rS2'` : type 2 criteria of "S2" responses for meta-d' fit,
            RMS units.
        * `'logL'` : log likelihood of the data fit
        * `'est_HR2_rS1'` : estimated (from meta-d' fit) type 2 hit rates for S1
        responses.
        * `'obs_HR2_rS1'` : actual type 2 hit rates for S1 responses.
        * `'est_FAR2_rS1'` : estimated type 2 false alarm rates for S1 responses.
        * `'obs_FAR2_rS1'` : actual type 2 false alarm rates for S1 responses.
        * `'est_HR2_rS2'` : estimated type 2 hit rates for S2 responses.
        * `'obs_HR2_rS2'` : actual type 2 hit rates for S2 responses.
        * `'est_FAR2_rS2'` : estimated type 2 false alarm rates for S2 responses.
        * `'obs_FAR2_rS2'` : actual type 2 false alarm rates for S2 responses.

    """
    if nCriteria is None:
        nCriteria = int(2 * nRatings - 1)

    # parameters
    # meta-d' - 1
    # t2c - nCriteria-1
    # constrain type 2 criteria values, such that t2c(i) is always <= t2c(i+1)
    # -->  t2c(i+1) >= t2c(i) + 1e-5 (i.e. very small deviation from equality)
    A, ub, lb = [], [], []
    for ii in range(nCriteria - 2):
        tempArow: List[int] = []
        tempArow.extend(np.zeros(ii + 1))
        tempArow.extend([1, -1])
        tempArow.extend(np.zeros((nCriteria - 2) - ii - 1))
        A.append(tempArow)
        ub.append(-1e-5)
        lb.append(-np.inf)

    # lower bounds on parameters
    LB = []
    LB.append(-10.0)  # meta-d'
    LB.extend(-20 * np.ones((nCriteria - 1) // 2))  # criteria lower than t1c
    LB.extend(np.zeros((nCriteria - 1) // 2))  # criteria higher than t1c

    # upper bounds on parameters
    UB = []
    UB.append(10.0)  # meta-d'
    UB.extend(np.zeros((nCriteria - 1) // 2))  # criteria lower than t1c
    UB.extend(20 * np.ones((nCriteria - 1) // 2))  # criteria higher than t1c

    # set up initial guess at parameter values
    ratingHR = []
    ratingFAR = []
    for c in range(1, int(nRatings * 2)):
        ratingHR.append(sum(nR_S2[c:]) / sum(nR_S2))
        ratingFAR.append(sum(nR_S1[c:]) / sum(nR_S1))

    # obtain index in the criteria array to mark Type I and Type II criteria
    t1_index = nRatings - 1
    t2_index = list(set(list(range(0, 2 * nRatings - 1))) - set([t1_index]))

    d1 = (1 / s) * fninv(ratingHR[t1_index]) - fninv(ratingFAR[t1_index])
    meta_d1 = d1

    c1 = (-1 / (1 + s)) * (fninv(ratingHR) + fninv(ratingFAR))
    t1c1 = c1[t1_index]
    t2c1 = c1[t2_index]

    # initial values for the minimization function
    guess = [meta_d1]
    guess.extend(list(t2c1 - (meta_d1 * (t1c1 / d1))))

    # other inputs for the minimization function
    bounds = Bounds(LB, UB)
    linear_constraint = LinearConstraint(A, lb, ub)

    # minimization of negative log-likelihood
    results = minimize(
        fit_meta_d_logL,
        guess,
        args=(nR_S1, nR_S2, nRatings, d1, t1c1, s),
        method="trust-constr",
        jac="2-point",
        hess=SR1(),
        constraints=[linear_constraint],
        options={"verbose": verbose},
        bounds=bounds,
    )

    # quickly process some of the output
    meta_d1 = results.x[0]
    t2c1 = results.x[1:] + (meta_d1 * (t1c1 / d1))
    logL = -results.fun

    # I_nR and C_nR are rating trial counts for incorrect and correct trials
    # element i corresponds to # (in)correct w/ rating i
    I_nR_rS2 = nR_S1[nRatings:]
    I_nR_rS1 = list(np.flip(nR_S2[0:nRatings], axis=0))

    C_nR_rS2 = nR_S2[nRatings:]
    C_nR_rS1 = list(np.flip(nR_S1[0:nRatings], axis=0))

    obs_FAR2_rS2 = [
        sum(I_nR_rS2[(i + 1) :]) / sum(I_nR_rS2) for i in range(nRatings - 1)
    ]
    obs_HR2_rS2 = [
        sum(C_nR_rS2[(i + 1) :]) / sum(C_nR_rS2) for i in range(nRatings - 1)
    ]
    obs_FAR2_rS1 = [
        sum(I_nR_rS1[(i + 1) :]) / sum(I_nR_rS1) for i in range(nRatings - 1)
    ]
    obs_HR2_rS1 = [
        sum(C_nR_rS1[(i + 1) :]) / sum(C_nR_rS1) for i in range(nRatings - 1)
    ]

    # find estimated t2FAR and t2HR
    S1mu = -meta_d1 / 2
    S1sd = 1
    S2mu = meta_d1 / 2
    S2sd = S1sd / s

    mt1c1 = meta_d1 * (t1c1 / d1)

    C_area_rS2 = 1 - fncdf(mt1c1, S2mu, S2sd)
    I_area_rS2 = 1 - fncdf(mt1c1, S1mu, S1sd)

    C_area_rS1 = fncdf(mt1c1, S1mu, S1sd)
    I_area_rS1 = fncdf(mt1c1, S2mu, S2sd)

    est_FAR2_rS2, est_HR2_rS2 = [], []
    est_FAR2_rS1, est_HR2_rS1 = [], []

    for i in range(nRatings - 1):

        t2c1_lower = t2c1[(nRatings - 1) - (i + 1)]
        t2c1_upper = t2c1[(nRatings - 1) + i]

        I_FAR_area_rS2 = 1 - fncdf(t2c1_upper, S1mu, S1sd)
        C_HR_area_rS2 = 1 - fncdf(t2c1_upper, S2mu, S2sd)

        I_FAR_area_rS1 = fncdf(t2c1_lower, S2mu, S2sd)
        C_HR_area_rS1 = fncdf(t2c1_lower, S1mu, S1sd)

        est_FAR2_rS2.append(I_FAR_area_rS2 / I_area_rS2)
        est_HR2_rS2.append(C_HR_area_rS2 / C_area_rS2)

        est_FAR2_rS1.append(I_FAR_area_rS1 / I_area_rS1)
        est_HR2_rS1.append(C_HR_area_rS1 / C_area_rS1)

    # package output
    fit = {}
    fit["dprime"] = np.sqrt(2 / (1 + s**2)) * s * d1
    fit["s"] = s
    fit["meta_d"] = np.sqrt(2 / (1 + s**2)) * s * meta_d1
    fit["m_diff"] = fit["meta_d"] - fit["dprime"]
    fit["m_ratio"] = fit["meta_d"] / fit["dprime"]

    fit["meta_ca"] = (np.sqrt(2) * s / np.sqrt(1 + s**2)) * mt1c1

    t2ca = (np.sqrt(2) * s / np.sqrt(1 + s**2)) * np.array(t2c1)
    fit["t2ca_rS1"] = t2ca[0 : nRatings - 1]
    fit["t2ca_rS2"] = t2ca[(nRatings - 1) :]

    fit["d1"] = d1
    fit["meta_d1"] = meta_d1
    fit["s"] = s
    fit["meta_c1"] = mt1c1
    fit["t2c1_rS1"] = t2c1[0 : nRatings - 1]
    fit["t2c1_rS2"] = t2c1[(nRatings - 1) :]
    fit["logL"] = logL

    fit["est_HR2_rS1"] = est_HR2_rS1
    fit["obs_HR2_rS1"] = obs_HR2_rS1

    fit["est_FAR2_rS1"] = est_FAR2_rS1
    fit["obs_FAR2_rS1"] = obs_FAR2_rS1

    fit["est_HR2_rS2"] = est_HR2_rS2
    fit["obs_HR2_rS2"] = obs_HR2_rS2

    fit["est_FAR2_rS2"] = est_FAR2_rS2
    fit["obs_FAR2_rS2"] = obs_FAR2_rS2

    return fit


import torch
from vocos import Vocos
import torchaudio
import torch
import gradio as gr
import tempfile
from src.bark.npz_tools import load_npz

def get_device():
    return torch.device("cuda" if torch.cuda.is_available() else "cpu")

vocos_model = None

def reconstruct_with_vocos(audio_tokens):
    device = get_device()
    global vocos_model
    if vocos_model is None:
        vocos_model = Vocos.from_pretrained("charactr/vocos-encodec-24khz").to(device)
    vocos = vocos_model
    audio_tokens_torch = torch.from_numpy(audio_tokens).to(device)
    features = vocos.codes_to_features(audio_tokens_torch)
    return vocos.decode(
        features, bandwidth_id=torch.tensor([2], device=device)
    )  # 6 kbps

def upsample_to_44100(audio):
    return torchaudio.functional.resample(audio, orig_freq=24000, new_freq=44100)

def vocos_tab_bark():
    with gr.Tab("Vocos (NPZ)"):
        npz_file = gr.File(label="Input NPZ", file_types=[".npz"], interactive=True)
        submit = gr.Button(value="Decode")
        current = gr.Audio(label="decoded with Encodec:")
        output = gr.Audio(label="decoded with Vocos:")

        from src.bark.get_audio_from_npz import get_audio_from_full_generation

        def get_audio(npz_file_obj: tempfile._TemporaryFileWrapper):
            if npz_file_obj is None:
                print("No file selected")
                return [None, None]

            full_generation = load_npz(npz_file_obj.name)
            return [
                get_audio_from_full_generation(full_generation),  # type: ignore
                None,
            ]

        npz_file.change(
            fn=get_audio,
            inputs=[npz_file],
            outputs=[current, output],
        )

        def vocos_predict(npz_file_obj: tempfile._TemporaryFileWrapper):
            if npz_file_obj is None:
                print("No file selected")
                return None

            full_generation = load_npz(npz_file_obj.name)
            audio_tokens = full_generation["fine_prompt"]
            vocos_output = reconstruct_with_vocos(audio_tokens)
            vocos_output = upsample_to_44100(vocos_output)
            return (44100, vocos_output.cpu().squeeze().numpy())

        submit.click(
            fn=vocos_predict,
            inputs=[npz_file],
            outputs=output,
        )

if __name__ == "__main__":
    with gr.Blocks() as demo:
        vocos_tab_bark()

    demo.launch(server_port=7863)


#!/anaconda3/bin/python3
#http://rosalind.info/problems/gc/
# This script reads in a fasta text file
# calculates the GC content of each sequence
# returns the sequence ID for the seq with the highest GC content AND
# the GC content of that sequence
# note: Rosalind allows for a default error of 0.001 in all decimal answers

from collections import defaultdict
import sys

with open(sys.argv[1], 'r') as fasta_file:
    data = fasta_file.read()

data_lines = data.split('\n')

seq_dict = defaultdict(str) #will look like {"ID": "sequence"} after the for loop below
id = '' #>accession-no stored without the ">". Overwritten after each new ">" line

#parse the data file
for line in data_lines:

    if line.startswith(">"):
        id = line[1:]
    else:
        seq_dict[id] += line

gc_dict = {} #{"ID": <gc content>}

#calculate GC content and add to new dictionary
for seq_id, seq in seq_dict.items():
    gc_count = 0
    for letter in seq:
        if letter == "G" or letter == "C":
            gc_count += 1

    gc_dict[seq_id] =  gc_count / len(seq) * 100

high_score = 0
winner = ''

for new_id, gc in gc_dict.items():
    if gc > high_score:
        high_score = gc
        winner = new_id

print(winner)
print(high_score)


__author__ = "Wren J. R. (uberfastman)"
__email__ = "uberfastman@uberfastman.dev"

import itertools
from collections import defaultdict, OrderedDict
from statistics import mean
from typing import Union, List, Dict, Any

import numpy as np

from dao.base import BaseLeague, BaseTeam, BaseRecord, BasePlayer
from report.logger import get_logger
from utilities.config import AppConfigParser

logger = get_logger(__name__, propagate=False)

class CalculateMetrics(object):
    def __init__(self, config: Union[AppConfigParser, None], league_id: Union[str, None],
                 playoff_slots: Union[int, None], playoff_simulations: Union[int, None]):
        logger.debug("Initializing metrics calculator.")

        self.config: AppConfigParser = config
        self.league_id: str = league_id
        self.playoff_slots: int = playoff_slots
        self.playoff_simulations: int = playoff_simulations
        self.coaching_efficiency_dq_count: int = 0

    @staticmethod
    def decode_byte_string(byte_string: Union[bytes, str]) -> Union[bytes, str]:
        try:
            return byte_string.decode("utf-8")
        except (UnicodeDecodeError, AttributeError):
            return byte_string

    @staticmethod
    def get_standings_data(league: BaseLeague) -> List[List[Any]]:
        logger.debug("Creating league standings data.")

        current_standings_data = []
        team: BaseTeam
        for team in league.standings:
            team_current_standings_data = [
                team.record.rank,
                team.name,
                team.manager_str,
                (f"{team.record.get_wins()}-{team.record.get_losses()}-{team.record.get_ties()} "
                 f"({team.record.get_percentage()})"),
                f"{team.record.get_points_for():.2f}",
                f"{team.record.get_points_against():.2f}",
                team.record.get_streak_str(),
                team.waiver_priority if not league.is_faab else f"${int(team.faab)}",
                team.num_moves,
                team.num_trades
            ]
            if league.is_faab:
                team_current_standings_data.insert(7, team.waiver_priority)

            current_standings_data.append(team_current_standings_data)

        return current_standings_data

    @staticmethod
    def get_division_standings_data(league: BaseLeague) -> List[List[List[Any]]]:
        logger.debug("Creating league division standings data.")

        # group teams into divisions
        division_groups = [
            list(group) for key, group in itertools.groupby(
                sorted(league.standings, key=lambda x: x.division),
                lambda x: str(x.division))
        ]

        # sort the teams
        sorted_divisions = {}
        for division_num in range(1, league.num_divisions + 1):
            sorted_divisions[division_num] = sorted(
                division_groups[division_num - 1],
                key=lambda x: (
                    x.record.get_wins(),
                    -x.record.get_losses(),
                    x.record.get_ties(),
                    x.record.get_division_wins(),
                    -x.record.get_division_losses(),
                    x.record.get_division_ties(),
                    float(x.record.get_points_for())
                ),
                reverse=True
            )

        # TODO: figure out how to get this ranking right
        ranked_division_teams = []
        for division in sorted_divisions.values():
            for team in division:
                ranked_division_teams.append(team)
        ranked_division_teams = sorted(
            ranked_division_teams,
            key=lambda x: (
                x.record.get_wins(),
                -x.record.get_losses(),
                x.record.get_ties(),
                x.record.get_division_wins(),
                -x.record.get_division_losses(),
                x.record.get_division_ties(),
                float(x.record.get_points_for())
            ),
            reverse=True
        )
        team_ranks_by_id = {}
        rank = 1
        for team in ranked_division_teams:
            team_ranks_by_id[team.team_id] = rank
            rank += 1

        modified_team_names = defaultdict()
        current_division_standings_data = []
        for division in sorted_divisions.values():
            division_standings_data = []
            team: BaseTeam
            for team in division:
                if division.index(team) == 0:
                    modified_team_names[team.team_id] = "†"
                else:
                    modified_team_names[team.team_id] = ""

                team_division_standings_data = [
                    team_ranks_by_id[team.team_id],
                    team.name + modified_team_names[team.team_id],
                    team.manager_str,
                    (f"{team.record.get_wins()}-{team.record.get_losses()}-{team.record.get_ties()} "
                     f"({team.record.get_percentage()})"),
                    (f"{team.record.get_division_wins()}-{team.record.get_division_losses()}"
                     f"-{team.record.get_division_ties()} ({team.record.get_division_percentage()})"),
                    f"{team.record.get_points_for():.2f}",
                    f"{team.record.get_points_against():.2f}",
                    team.record.get_streak_str(),
                    team.waiver_priority if not league.is_faab else f"${int(team.faab)}",
                    team.num_moves,
                    team.num_trades,
                    str(team.division)  # stored here temporarily to pass team divisions to report generator
                ]
                if league.is_faab and team.waiver_priority != 0:
                    team_division_standings_data.insert(8, team.waiver_priority)

                division_standings_data.append(team_division_standings_data)

            current_division_standings_data.append(division_standings_data)
        return current_division_standings_data

    @staticmethod
    def get_median_standings_data(league: BaseLeague) -> List[List[Any]]:
        logger.debug("Creating league median standings data.")

        current_median_standings_data = []
        rank = 1
        team: BaseTeam
        for team in sorted(
                league.current_median_standings,
                key=lambda x: (
                        x.get_combined_record().get_wins(),
                        -x.get_combined_record().get_losses(),
                        x.get_combined_record().get_ties(),
                        x.get_combined_record().get_points_for()
                ),
                reverse=True
        ):
            combined_record = team.get_combined_record()
            team_current_median_standings_data = [
                rank,
                team.name,
                team.manager_str,
                (
                        str(combined_record.get_wins())
                        + "-" + str(combined_record.get_losses())
                        + "-" + str(combined_record.get_ties())
                        + " (" + str(combined_record.get_percentage()) + ")"
                ),
                (
                        str(team.current_median_record.get_wins())
                        + "-" + str(team.current_median_record.get_losses())
                        + "-" + str(team.current_median_record.get_ties())
                        + " (" + str(team.current_median_record.get_percentage()) + ")"
                ),
                f"{round(float(team.current_median_record.get_points_for()), 2):.2f}",
                team.current_median_record.get_streak_str(),
                f"{team.current_median_record.get_points_against():.2f}"
            ]

            current_median_standings_data.append(team_current_median_standings_data)
            rank += 1

        return current_median_standings_data

    @staticmethod
    def get_playoff_probs_data(league_standings: List[BaseTeam],
                               data_for_playoff_probs: Dict[str, List[Any]]) -> List[List[Any]]:
        logger.debug("Creating league playoff probabilities data.")

        has_divisions = False
        playoff_probs_data = []
        team: BaseTeam
        for team in league_standings:

            # sum rolling place percentages together to get a cumulative percentage chance of achieving that place
            # summed_stats = []
            # ndx = 1
            team_with_playoff_probs = data_for_playoff_probs[team.team_id]
            team_playoff_stats = team_with_playoff_probs[2]
            # while ndx <= len(team_playoff_stats):
            #     summed_stats.append(sum(team_playoff_stats[:ndx]))
            #     ndx += 1
            # if summed_stats[-1] > 100.00:
            #     summed_stats[-1] = 100.00
            if team_playoff_stats[-1] > 100.00:
                team_playoff_stats[-1] = 100.00

            team_playoffs_data = [
                                     team_with_playoff_probs[0],
                                     team.manager_str,
                                     str(team.record.get_wins()) + "-" + str(team.record.get_losses()) + "-" +
                                     str(team.record.get_ties()) + " (" + str(team.record.get_percentage()) + ")",
                                     team_with_playoff_probs[1],
                                     team_with_playoff_probs[3]
                                 ] + team_playoff_stats
            # ] + summed_stats

            if team.record.division or team.record.division == 0:
                has_divisions = True
                team_playoffs_data.insert(
                    3,
                    str(team.record.get_division_wins()) + "-" + str(team.record.get_division_losses()) + "-" +
                    str(team.record.get_division_ties()) + " (" + str(team.record.get_division_percentage()) + ")",
                )

            # add value for if team was predicted division winner to pass to the later sort function
            team_playoffs_data.append(team_with_playoff_probs[4])

            # add value for if team was predicted division qualifier to pass to the later sort function
            team_playoffs_data.append(team_with_playoff_probs[5])

            playoff_probs_data.append(team_playoffs_data)

        prob_ndx = 3
        if has_divisions:
            prob_ndx = 4

        sorted_playoff_probs_data = sorted(playoff_probs_data, key=lambda x: (x[-2], x[-1], x[prob_ndx]), reverse=True)
        for team_playoff_probs_data in sorted_playoff_probs_data:
            team_playoff_probs_data.pop(-1)  # remove "division qualifier" bool (original index: -1)
            team_playoff_probs_data.pop(-1)  # remove "division winner" bool (original index: -2)
            team_playoff_probs_data[prob_ndx] = f"{team_playoff_probs_data[prob_ndx]:.2f}%"
            if team_playoff_probs_data[prob_ndx + 1] == 1:
                team_playoff_probs_data[prob_ndx + 1] = f"{int(float(team_playoff_probs_data[prob_ndx + 1]))} win"
            else:
                team_playoff_probs_data[prob_ndx + 1] = f"{int(float(team_playoff_probs_data[prob_ndx + 1]))} wins"
            ndx = prob_ndx + 2
            for stat in team_playoff_probs_data[prob_ndx + 2:]:
                team_playoff_probs_data[ndx] = f"{stat:.2f}%"
                ndx += 1

        return sorted_playoff_probs_data

    @staticmethod
    def get_score_data(score_results: List[BaseTeam]) -> List[List[Any]]:
        logger.debug("Creating league score data.")

        score_results_data = []
        place = 1
        team: BaseTeam
        for team in score_results:
            ranked_team_name = team.name
            ranked_team_manager = team.manager_str
            ranked_weekly_score = f"{team.points:.2f}"
            ranked_weekly_bench_score = f"{team.bench_points:.2f}"

            score_results_data.append(
                [place, ranked_team_name, ranked_team_manager, ranked_weekly_score, ranked_weekly_bench_score])

            place += 1

        return score_results_data

    def get_coaching_efficiency_data(self, coaching_efficiency_results: List[BaseTeam]) -> List[List[Any]]:
        logger.debug("Creating league coaching efficiency data.")

        coaching_efficiency_results_data = []
        place = 1
        team: BaseTeam
        for team in coaching_efficiency_results:
            ranked_team_name = team.name
            ranked_team_manager = team.manager_str
            ranked_coaching_efficiency = team.coaching_efficiency

            if ranked_coaching_efficiency == "DQ":
                self.coaching_efficiency_dq_count += 1
            else:
                ranked_coaching_efficiency = f"{ranked_coaching_efficiency:.2f}%"

            coaching_efficiency_results_data.append(
                [place, ranked_team_name, ranked_team_manager, ranked_coaching_efficiency])

            place += 1

        return coaching_efficiency_results_data

    @staticmethod
    def get_luck_data(luck_results: List[BaseTeam]) -> List[List[Any]]:
        logger.debug("Creating league luck data.")

        luck_results_data = []
        place = 1
        team: BaseTeam
        for team in luck_results:
            ranked_team_name = team.name
            ranked_team_manager = team.manager_str
            ranked_luck = f"{team.luck:.2f}%"
            weekly_overall_record = team.weekly_overall_record.get_record_str()

            luck_results_data.append([place, ranked_team_name, ranked_team_manager, ranked_luck, weekly_overall_record])

            place += 1
        return luck_results_data

    @staticmethod
    def get_optimal_score_data(score_results: List[BaseTeam]) -> List[List[Any]]:
        logger.debug("Creating league optimal score data.")

        optimal_score_results_data = []
        place = 1
        team: BaseTeam
        for team in score_results:
            ranked_team_name = team.name
            ranked_team_manager = team.manager_str
            ranked_weekly_optimal_score = f"{team.optimal_points:.2f}"

            optimal_score_results_data.append(
                [place, ranked_team_name, ranked_team_manager, ranked_weekly_optimal_score])

            place += 1

        return optimal_score_results_data

    @staticmethod
    def get_bad_boy_data(bad_boy_results: List[BaseTeam]) -> List[List[Any]]:
        logger.debug("Creating league bad boys data.")

        bad_boy_results_data = []
        place = 1
        team: BaseTeam
        for team in bad_boy_results:
            ranked_team_name = team.name
            ranked_team_manager = team.manager_str
            ranked_bb_points = str(team.bad_boy_points)
            ranked_offense = team.worst_offense
            ranked_count = str(team.num_offenders)

            bad_boy_results_data.append(
                [place, ranked_team_name, ranked_team_manager, ranked_bb_points, ranked_offense, ranked_count]
            )

            place += 1
        return bad_boy_results_data

    @staticmethod
    def get_beef_rank_data(beef_results: List[BaseTeam]) -> List[List[Any]]:
        logger.debug("Creating league beef data.")

        beef_results_data = []
        place = 1
        team: BaseTeam
        for team in beef_results:
            ranked_team_name = team.name
            ranked_team_manager = team.manager_str
            ranked_beef_points = f"{team.tabbu:.3f}"

            beef_results_data.append([place, ranked_team_name, ranked_team_manager, ranked_beef_points])
            place += 1
        return beef_results_data

    @staticmethod
    def get_covid_risk_rank_data(covid_risk_results: List[BaseTeam]) -> List[List[Any]]:
        logger.debug("Creating league COVID-19 risk data.")

        covid_risk_data = []
        ndx = 0
        team: BaseTeam
        for team in covid_risk_results:
            ranked_team_name = team.name
            ranked_team_manager = team.manager_str
            ranked_covid_risk = str(team.total_covid_risk)

            covid_risk_data.append([ndx, ranked_team_name, ranked_team_manager, ranked_covid_risk])
            ndx += 1
        return covid_risk_data

    def get_ties_count(self, results_data: List[List[Any]], tie_type: str, break_ties: bool) -> int:

        if tie_type == "power_ranking":
            groups = [list(group) for key, group in itertools.groupby(results_data, lambda x: x[0])]
            num_ties = self.count_ties(groups)
        else:
            groups = [list(group) for key, group in itertools.groupby(results_data, lambda x: x[3])]
            num_ties = self.count_ties(groups)

        # if there are ties, record them and break them if possible
        if num_ties > 0:
            ties_count = 0
            team_index = 0
            place = 1
            while ties_count != num_ties:
                for group in groups:
                    group_has_ties = len(group) > 1 and "DQ" not in group[0]
                    if group_has_ties:
                        ties_count += sum(range(len(group)))

                    for team in group:
                        if tie_type == "power_ranking":
                            results_data[team_index] = [
                                str(team[0]) + ("*" if group_has_ties else ""),
                                team[1],
                                team[2],
                            ]
                        elif tie_type == "score" and break_ties:
                            results_data[team_index] = [
                                str(place),
                                team[1],
                                team[2],
                                team[3]
                            ]
                            if group.index(team) != (len(group) - 1):
                                place += 1
                        elif tie_type == "bad_boy":
                            results_data[team_index] = [
                                str(place) + ("*" if group_has_ties else ""),
                                team[1],
                                team[2],
                                team[3],
                                team[4],
                                team[5]
                            ]
                        else:
                            results_data[team_index] = [
                                str(place) + ("*" if group_has_ties else ""),
                                team[1],
                                team[2],
                                team[3]
                            ]

                        if tie_type == "score":
                            results_data[team_index].append(team[4])

                        team_index += 1
                    place += 1

        if tie_type == "bad_boy":
            groups = [list(group) for key, group in itertools.groupby(results_data, lambda x: x[3])]
            num_ties = 0
            for group in groups:
                if len(group) > 1 and int(group[0][3]) > 0:
                    num_ties += sum(range(len(group)))

        return num_ties

    @staticmethod
    def count_ties(groups: List[List[Any]]) -> int:
        num_ties = 0
        for group in groups:
            if len(group) > 1 and "DQ" not in group[0]:
                num_ties += sum(range(len(group)))

        return num_ties

    @staticmethod
    def resolve_score_ties(data_for_scores: List[List[Any]], break_ties: bool) -> List[List[Any]]:

        groups = [list(group) for key, group in itertools.groupby(data_for_scores, lambda x: x[3])]

        resolved_score_results_data = []
        place = 1
        for group in groups:
            for team in sorted(group, key=lambda x: x[-1], reverse=True):
                if groups.index(group) != 0:
                    team[0] = place
                else:
                    if break_ties:
                        team[0] = place
                resolved_score_results_data.append(team)
                place += 1

        return resolved_score_results_data

    @staticmethod
    def resolve_coaching_efficiency_ties(data_for_coaching_efficiency: List[List[Any]],
                                         ties_for_coaching_efficiency: int, league: BaseLeague,
                                         teams_results: Dict[str, BaseTeam], week: int, week_for_report: int,
                                         break_ties: bool) -> List[List[Any]]:

        logger.debug("Resolving coaching efficiency ties.")

        if league.player_data_by_week_function:
            coaching_efficiency_results_data_with_tiebreakers = []
            bench_positions = league.bench_positions

            season_average_points_by_player_dict = defaultdict(list)
            if break_ties and ties_for_coaching_efficiency > 0 and week == int(week_for_report):
                for ce_result in data_for_coaching_efficiency:
                    if ce_result[0] == "1*":
                        players = []
                        for team_result in teams_results.values():
                            if team_result.name == ce_result[1]:
                                players = teams_results.get(team_result.team_id).roster

                        num_players_exceeded_season_avg_points = 0
                        total_percentage_points_players_exceeded_season_avg_points = 0
                        player: BasePlayer
                        for player in players:
                            if player.selected_position not in bench_positions:
                                week_counter = 1
                                while week_counter <= int(week):
                                    players_by_week = league.players_by_week[str(week_counter)]
                                    if str(player.player_id) in players_by_week.keys():
                                        weekly_player_points = players_by_week[str(player.player_id)].points
                                    else:
                                        weekly_player_points = league.get_player_data_by_week(
                                            str(player.player_id), week_counter)

                                    season_average_points_by_player_dict[player.player_id].append(weekly_player_points)
                                    week_counter += 1

                                player_last_week_points = season_average_points_by_player_dict[player.player_id][-1]

                                # handle the beginning of the season when a player has only played one or no games
                                player_season_weekly_points = season_average_points_by_player_dict[player.player_id]
                                if len(player_season_weekly_points) == 0:
                                    player_season_avg_points = 0
                                elif len(player_season_weekly_points) == 1:
                                    player_season_avg_points = player_season_weekly_points[0]
                                else:
                                    player_season_avg_points = mean(player_season_weekly_points[:-1])

                                if player_last_week_points > player_season_avg_points:
                                    num_players_exceeded_season_avg_points += 1

                                    if player_season_avg_points > 0:
                                        total_percentage_points_players_exceeded_season_avg_points += (
                                                (
                                                        (player_last_week_points - player_season_avg_points)
                                                        / player_season_avg_points
                                                ) * 100.0

                                        )
                                    else:
                                        total_percentage_points_players_exceeded_season_avg_points += 100.0

                        ce_result.extend([num_players_exceeded_season_avg_points,
                                          round(total_percentage_points_players_exceeded_season_avg_points, 2)])
                        coaching_efficiency_results_data_with_tiebreakers.append(ce_result)
                    else:
                        ce_result.extend(["N/A", "N/A"])
                        coaching_efficiency_results_data_with_tiebreakers.append(ce_result)

                groups = [list(group) for key, group in
                          itertools.groupby(coaching_efficiency_results_data_with_tiebreakers, lambda x: x[3])]
            else:
                groups = [list(group) for key, group in itertools.groupby(data_for_coaching_efficiency, lambda x: x[3])]

            resolved_coaching_efficiency_results_data = []
            place = 1
            for group in groups:
                for team in sorted(
                        group,
                        key=lambda x: (x[-2] if x[-2] != "DQ" else 0, x[-1]),
                        reverse=True):
                    if groups.index(group) == 0:
                        if break_ties:
                            team[0] = place
                    resolved_coaching_efficiency_results_data.append(team)
                    place += 1
            return resolved_coaching_efficiency_results_data
        else:
            logger.debug(
                "No function to retrieve past player weekly points available. Cannot resolve coaching efficiency ties.")
            return data_for_coaching_efficiency

    @staticmethod
    def resolve_season_average_ties(data_for_season_averages: List[List[Any]], with_percent: bool) -> List[List[Any]]:

        groups = [list(group) for key, group in itertools.groupby(data_for_season_averages, lambda x: x[2])]

        resolved_season_average_results_data = []
        place = 1
        for group in groups:
            for team in sorted(group, key=lambda x: x[-1], reverse=True):
                team[0] = place
                if with_percent:
                    team[2] = f"{team[2]}% ({place})"
                else:
                    team[2] = f"{team[2]} ({place})"

                resolved_season_average_results_data.append(team)
            place += 1

        return resolved_season_average_results_data

    # noinspection PyUnusedLocal
    @staticmethod
    def test_ties(teams_results):
        for team_id, team in teams_results.items():

            team_id = team.team_id

            # for testing score ties
            test_score = 70
            test_efficiency = 75.00
            test_luck = 10.00
            test_power_rank = 5.0

            # swap the first team to test for non-first place ties vs. first place ties
            # if int(team_id) == 1:
            #     test_score = 101
            #     test_efficiency = 101.00
            #     test_luck = 99.50
            #     test_power_rank = 0.9
            if int(team_id) == 1:
                test_score = 100
                test_efficiency = 100.00
                test_luck = 99.00
                test_power_rank = 1.0
            elif int(team_id) == 2:
                test_score = 100
                test_efficiency = 100.00
                test_luck = 99.00
                test_power_rank = 1.0
            # swap the third time to test for middle ranked non-ties
            # elif int(team_id) == 3:
            #     test_score = 100
            #     test_efficiency = 100.00
            #     test_luck = 99.00
            #     test_power_rank = 1.0
            elif int(team_id) == 3:
                test_score = 95
                test_efficiency = 95.00
                test_luck = 50.00
                test_power_rank = 1.5
            elif int(team_id) == 4:
                test_score = 90
                test_efficiency = 90.00
                test_luck = 5.00
                test_power_rank = 2.0
            elif int(team_id) == 5:
                test_score = 90
                test_efficiency = 90.00
                test_luck = 5.00
                test_power_rank = 2.0
            elif int(team_id) == 6:
                test_score = 90
                test_efficiency = 90.00
                test_luck = 5.00
                test_power_rank = 2.0
            # uncomment to test ending teams with unique place
            elif int(team_id) == len(list(teams_results.keys())):
                test_score = 85
                test_efficiency = 85.00
                test_luck = -5.00
                test_power_rank = 6.0

            # # uncomment to test scoring ties
            # team.score = test_score
            #
            # # uncomment to test coaching efficiency ties
            # team.coaching_efficiency = test_efficiency
            #
            # # # uncomment to test luck ties
            # team.luck = test_luck
            #
            # # # uncomment to test power ranking ties
            # team.power_rank = test_power_rank

    @staticmethod
    def calculate_records(week: int, league: BaseLeague,
                          custom_weekly_matchups: List[Dict[str, Dict[str, Any]]]) -> Dict[str, BaseRecord]:
        logger.debug(f"Calculating league records for week \"{week}\".")

        standings = league.standings if league.standings else league.current_standings

        records = defaultdict(BaseRecord)
        team: BaseTeam
        for team in standings:
            if week == league.start_week:
                record = BaseRecord(week, team_id=team.team_id, team_name=team.name, division=team.division)
            else:
                previous_week_record: BaseRecord = league.records_by_week[str(week - 1)][team.team_id]
                record = BaseRecord(
                    week,
                    wins=previous_week_record.get_wins(),
                    ties=previous_week_record.get_ties(),
                    losses=previous_week_record.get_losses(),
                    points_for=previous_week_record.get_points_for(),
                    points_against=previous_week_record.get_points_against(),
                    streak_type=previous_week_record.get_streak_type(),
                    streak_len=previous_week_record.get_streak_length(),
                    team_id=team.team_id,
                    team_name=team.name,
                    division=team.division,
                    division_wins=previous_week_record.get_division_wins(),
                    division_ties=previous_week_record.get_division_ties(),
                    division_losses=previous_week_record.get_division_losses(),
                    division_points_for=previous_week_record.get_division_points_for(),
                    division_points_against=previous_week_record.get_division_points_against(),
                    division_streak_type=previous_week_record.get_division_streak_type(),
                    division_streak_len=previous_week_record.get_division_streak_length()
                )

            # league.matchups_by_week[str(week)]

            for matchup in custom_weekly_matchups:
                for team_id, matchup_result in matchup.items():
                    if str(team_id) == str(team.team_id):
                        outcome = matchup_result["result"]
                        if outcome == "W":
                            record.add_win()
                            if matchup_result["division"]:
                                record.add_division_win()
                        elif outcome == "L":
                            record.add_loss()
                            if matchup_result["division"]:
                                record.add_division_loss()
                        else:
                            record.add_tie()
                            if matchup_result["division"]:
                                record.add_division_tie()
                        record.add_points_for(matchup_result["points_for"])
                        record.add_points_against(matchup_result["points_against"])
                        if matchup_result["division"]:
                            record.add_division_points_for(matchup_result["points_for"])
                            record.add_division_points_against(matchup_result["points_against"])
                        records[team.team_id] = record

            team.record = record

        ordered_records = OrderedDict()
        standings_rank = 1
        for ordered_record in sorted(
                records.items(),
                key=lambda x: (-x[1].get_wins(), -x[1].get_losses(), -x[1].get_ties(), -x[1].get_points_for())):
            ordered_record[1].rank = standings_rank
            ordered_records[ordered_record[0]] = ordered_record[1]
            standings_rank += 1

        league.records_by_week[str(week)] = ordered_records
        return records

    @staticmethod
    def calculate_luck(week: int, league: BaseLeague, custom_weekly_matchups: List[Dict[str, Dict[str, Any]]]) -> Dict[
        str, Dict[str, BaseRecord]]:
        logger.debug(f"Calculating luck for week \"{week}\".")

        luck_results = defaultdict(defaultdict)

        teams = league.teams_by_week.get(str(week))

        matchups = {
            str(team_id): value[
                "result"] for pair in custom_weekly_matchups for team_id, value in list(pair.items())
        }

        team_1: BaseTeam
        for team_1 in teams.values():
            luck_record = BaseRecord()

            for team_2 in teams.values():
                if team_1.team_id == team_2.team_id:
                    continue
                score_1 = team_1.points
                score_2 = team_2.points

                if float(score_1) > float(score_2):
                    luck_record.add_win()
                elif float(score_1) < float(score_2):
                    luck_record.add_loss()
                else:
                    luck_record.add_tie()

            luck_results[team_1.team_id]["luck_record"] = luck_record

            # calc luck %
            # TODO: assuming no ties...  how are tiebreakers handled?
            luck = 0.0
            # number of teams excluding current team
            num_teams = float(len(teams)) - 1

            if luck_record.get_wins() != 0 and luck_record.get_losses() != 0:
                matchup_result = matchups[str(team_1.team_id)]
                if matchup_result == "W" or matchup_result == "T":
                    luck = (luck_record.get_losses() + luck_record.get_ties()) / num_teams
                else:
                    luck = 0 - (luck_record.get_wins() + luck_record.get_ties()) / num_teams

            # noinspection PyTypeChecker
            luck_results[team_1.team_id]["luck"] = luck * 100

        return luck_results

    @staticmethod
    def get_ranks_for_metric(data_for_metric: List[List[Any]], power_ranked_teams: Dict[str, Dict[str, Any]],
                             metric_ranking_key: str):
        rank = 1
        for team in data_for_metric:
            for team_rankings in power_ranked_teams.values():
                if team[1] == team_rankings["name"]:
                    team_rankings[metric_ranking_key] = rank
            rank += 1

    def calculate_power_rankings(self, teams_results: Dict[str, BaseTeam], data_for_scores: List[List[Any]],
                                 data_for_coaching_efficiency: List[List[Any]],
                                 data_for_luck: List[List[Any]]) -> Dict[str, Dict[str, Any]]:
        """ avg of (weekly score rank + weekly coaching efficiency rank + weekly luck rank)
        """
        logger.debug("Calculating power rankings.")

        power_ranked_teams = {
            team_result.team_id: {
                "name": team_result.name,
                "manager_str": team_result.manager_str
            } for team_result in teams_results.values()
        }

        self.get_ranks_for_metric(data_for_scores, power_ranked_teams, "score_ranking")
        self.get_ranks_for_metric(data_for_coaching_efficiency, power_ranked_teams, "coaching_efficiency_ranking")
        self.get_ranks_for_metric(data_for_luck, power_ranked_teams, "luck_ranking")

        for team_rankings in power_ranked_teams.values():
            team_rankings["power_ranking"] = (team_rankings["score_ranking"] +
                                              team_rankings["coaching_efficiency_ranking"] +
                                              team_rankings["luck_ranking"]) // 3.0
        return power_ranked_teams

    @staticmethod
    def calculate_z_scores(weekly_teams_results: List[Dict[str, BaseTeam]]) -> Dict[str, float]:
        logger.debug("Calculating z-scores.")

        results = {}

        # can only determine z_score
        can_calculate = len(weekly_teams_results) > 2

        # iterates through team ids of first week since team ids remain unchanged
        for team_id in weekly_teams_results[0].keys():
            z_score = None

            if can_calculate:
                scores = [week[team_id].points for week in weekly_teams_results]

                scores_excluding_current = scores[:-1]
                current_score = scores[-1]

                standard_deviation = np.std(scores_excluding_current)
                mean_score = np.mean(scores_excluding_current)
                z_score = (float(current_score) - float(mean_score)) / float(
                    standard_deviation) if standard_deviation != 0 else 0

            results[team_id] = z_score

        return results


#!/usr/bin/env python

"""Simulate crystal X-ray diffraction pattern for given parameters.

Usage: simulator.py <SF-FILE> [options]

Options:
    -h --help               Show this screen.
    -n --num NUM            Specify number of patterns to simulate [default: 3].
    -o --output FILE        Specify output filename [default: peaks.npy].
    --lattice LATTICE       Specify 6 lattice parameters in angstrom and degree [default: 100,100,100,90,90,90].
    --det-dist DIST         Specify detector distance in meter [default: 0.1].
    --photon-energy PE      Specify single or multiple photon energy in eV [default: 9000,7000].
    --show                  Whether to display simulated patterns.
"""

import h5py
import numpy as np
from docopt import docopt
from numpy.linalg import norm
from tqdm import tqdm
import matplotlib.pyplot as plt
from util import calc_transform_matrix, calc_wavelength, euler_angles_to_rotation_matrix

BLOB_SIZE = 0.0002  # in per angstrom
FS = np.array([1., 0., 0.])  # detector fast scan vector
SS = np.array([0., 1., 0.])  # detector slow scan vector
PIXEL_SIZE = 100E-6  # pixel size in meter
CENTER_FS = 720  # beam center alone fast scan direction
CENTER_SS = 720  # beam center alone slow scan direction
SIZE_FS = 1440  # detector size alone fast scan direction
SIZE_SS = 1440  # detector size alone slow scan direction
EPSILON = 0.001

if __name__ == "__main__":
    argv = docopt(__doc__)
    det_dist = float(argv['--det-dist'])
    hkl_file = argv['<SF-FILE>']
    sf_data = np.loadtxt(hkl_file, skiprows=1)[:, 0:4]
    sf_data = np.concatenate([sf_data, -sf_data])  # expand sf list
    sf_data[:, 3] = np.abs(sf_data[:, 3])
    sf_data[:, 3] = sf_data[:, 3]/100  # scaling down intensity

    lattice_constants = list(map(float, argv['--lattice'].split(',')))
    A0 = calc_transform_matrix(lattice_constants)

    # photon energy
    PEs = sorted(list(map(float, argv['--photon-energy'].split(','))))
    N = int(argv['--num'])

    peak_data = []
    for i in range(N):
        reflections = None
        # random euler angles in degrees [0, 90]
        euler_angles = np.random.rand(3) * 90
        R = euler_angles_to_rotation_matrix(euler_angles)
        for PE in PEs:
            wavelength = calc_wavelength(PE) * 1E10
            # q vectors in standard system (centered on 000 miller indice)
            Q0 = A0.dot(sf_data[:, 0:3].T).T
            Q = R.dot(Q0.T).T  # q vectors after rotation
            ewald_radius = 1. / wavelength  # radius of Ewald sphere in angstrom
            Q[:, 2] += ewald_radius  # q vectors with center on scattering point
            # Q residue of a reflection to Ewald sphere in per angstrom
            Q_res = np.abs(norm(Q, axis=1) - ewald_radius)
            fired_idx = np.where(Q_res < BLOB_SIZE)[0]

            Q_fired = Q[fired_idx]
            Q_fired /= norm(Q_fired, axis=1)[:, np.newaxis]
            I_fired = sf_data[fired_idx, 3]
            # length of fired reflections in real space
            rho = det_dist / Q_fired[:, 2]
            # fired reflections in real space, centered on scattering point
            peaks = Q_fired * rho[:, np.newaxis]
            peaks_fs = (peaks - np.array([0., 0., det_dist])
                        ).dot(FS) / norm(FS) / PIXEL_SIZE + CENTER_FS
            peaks_ss = (peaks - np.array([0., 0., det_dist])
                        ).dot(SS) / norm(SS) / PIXEL_SIZE + CENTER_SS
            valid_idx = np.where((peaks_fs >= 0) & (peaks_fs < SIZE_FS) & (
                peaks_ss >= 0) & (peaks_ss < SIZE_SS))[0]
            peaks_fs = peaks_fs[valid_idx]
            peaks_ss = peaks_ss[valid_idx]

            _reflections = np.zeros((valid_idx.size, 12))
            _reflections[:, 0:4] = sf_data[fired_idx][valid_idx][:, 0:4]
            _reflections[:, 4] = Q_res[fired_idx][valid_idx]
            _reflections[:, 5] = peaks_fs
            _reflections[:, 6] = peaks_ss
            _reflections[:, 7] = np.sqrt(
                (peaks_ss - CENTER_SS) ** 2 + (peaks_fs - CENTER_FS) ** 2)
            _reflections[:, 8] = PE
            _reflections[:, 9] = (peaks_ss - CENTER_SS) * PIXEL_SIZE
            _reflections[:, 10] = (peaks_fs - CENTER_FS) * PIXEL_SIZE
            _reflections[:, 11] = I_fired  # peak intensity
            if reflections is None:
                reflections = _reflections
            else:
                reflections = np.concatenate([reflections, _reflections])

        peak_data.append({
            'clen': det_dist,
            'euler_angles': euler_angles,
            'rotation_matrix': R,
            'A': R.dot(A0),
            'peaks': reflections[:, 5:7],
            'photon_energy': reflections[:, 8],
            'coords': reflections[:, 9:11],
            'intensity': reflections[:, 11]
        })

        if argv['--show']:
            im = np.zeros((SIZE_SS, SIZE_FS))
            fig, ax = plt.subplots()
            ax.imshow(im)
            for i in range(len(PEs)):
                this_color_idx = np.where(
                    np.abs(reflections[:, 8] - PEs[i]) < EPSILON)[0]
                ax.scatter(
                    reflections[this_color_idx][:, 5],
                    reflections[this_color_idx][:, 6],
                    color='C%d' % i, s=3
                )
                print('peaks of color %d: %d' % (i, this_color_idx.size))
            ax.scatter(720, 720, color='red')
            ax.set_title(euler_angles)
            plt.show(block=True)

    # save to cxi file
    cxi = h5py.File('sim.cxi', 'w')
    cxi_data = [det_dist for item in peak_data]
    cxi_data = np.array(cxi_data)
    cxi.create_dataset('clen', data=cxi_data)
    cxi_data = [PEs[0] for item in peak_data]
    cxi_data = np.array(cxi_data)
    cxi.create_dataset('photon_energy', data=cxi_data)
    cxi_data = [item['A'] for item in peak_data]
    cxi_data = np.stack(cxi_data)
    cxi.create_dataset('A', data=cxi_data)
    cxi_data = []
    for item in peak_data:
        img = (np.random.rand(SIZE_SS, SIZE_FS) * 10).astype(np.int)
        for i, peak in enumerate(item['peaks']):
            peak = np.round(peak).astype(np.int)
            img[
                peak[0]-1:peak[0]+2,
                peak[1]-1:peak[1]+2
                ] = item['intensity'][i] / 9
            img[img > 32767] = 32767  # saturated peaks
        cxi_data.append(img)
    cxi_data = np.stack(cxi_data)
    cxi.create_dataset(
        'data', data=cxi_data,
        compression='gzip',
        compression_opts=3,
    )
    cxi.close()

    # np.save(argv['--output'], peak_data)
    # print('Peak file saved to %s' % argv['--output'])


import pandas as pd
import numpy as np
import Data_extraction_gui
import data_analysis_gui
from tkinter import *
import requests as re

def homeWindow():
	a=Tk()
	a.title("Trivago Analysis Tool")
	a.geometry("500x500")
	a.resizable(False, False)
	#a.eval('tk::PlaceWindow . center')

	def func1():
	    a.destroy()
	    Data_extraction_gui.triv1()
	def func2():
	    a.destroy()
	    data_analysis_gui.triv2()

	def exitFunc():
		a.destroy()

	l=Label(a,text="Click this button to extract data")
	l.place(relx=0.5,rely=0.1,anchor=CENTER)
	b=Button(a,text="Data Extract",bg="green",fg="white",command=func1)
	b.place(relx=0.5,rely=0.2,anchor=CENTER)
	l2=Label(a,text="Click this button to visualise data")
	l2.place(relx=0.5,rely=0.3,anchor=CENTER)
	b1=Button(a,text="Data Analysis",bg="green",fg="white",command=func2)
	b1.place(relx=0.5,rely=0.4,anchor=CENTER)
	b2=Button(a,text="Exit",bg="green",fg="white",command=exitFunc).place(relx=0.5,rely=0.5,anchor=CENTER)
	a.mainloop()

if __name__ == '__main__':
	try:
		page = re.get("https://www.google.com").status_code
		homeWindow()
	except:
		print("No Internet Connection!")

Trivago scrap and analysis/trivago_tk.py


#!/usr/bin/env python

import os
import sys
from pathlib import Path

asr_result = sys.argv[1]
mt_dir = sys.argv[2]
file = sys.argv[3]
output_dir = sys.argv[4]

def get_asr_dict(file):
    asr_dict = dict()
    with open(file, 'r') as f:
        for line in f.readlines():
            line_split = line.split()
            utt_id = line_split[0]
            sentence = " ".join(line_split[1:])
            assert(utt_id not in asr_dict)
            asr_dict[utt_id] = sentence

    return asr_dict

def get_utt_id(line_list):
    start_time = "".join(line_list[2].split('.'))
    start_digit = len(line_list[2].split('.')[1])
    start_dif = 3 - start_digit

    end_time = "".join(line_list[3].split('.'))
    end_digit = len(line_list[3].split('.')[1])
    end_dif = 3 - end_digit

    if start_time == '00':
        start_time = '0'
    else:
        start_time += "0" * start_dif
    end_time += "0" * end_dif

    utt_id = line_list[0] + "_" + start_time + "_" + end_time
    return utt_id

def write_result(asr_dict, file, mt_dir, output_dir):
    # create output directory
    output_path = Path(output_dir)
    output_path.mkdir(exist_ok=True)

    # read each file
    with open(file, 'r') as f:
        for line in f.readlines():
            file_name = line.split()[0]
            output_file_name = file_name.split('.')[0]
            with open(mt_dir+"/"+file_name, 'r') as input_file:
                with open(output_dir+"/"+output_file_name+".txt", 'w') as output_file:
                    for line in input_file.readlines():
                        line_split = line.split()
                        utt_id = get_utt_id(line_split)
                        if utt_id not in asr_dict:
                            print(utt_id)
                        else:
                            output_file.write(utt_id + " " + asr_dict[utt_id] + "\n")

def main():
    asr_dict = get_asr_dict(asr_result)
    write_result(asr_dict, file, mt_dir, output_dir)

if __name__ == "__main__":
    main()


import torch
import torch.autograd as autograd
import torch.nn as nn
from torch.autograd import Variable
import torch.nn.functional as F

use_cuda = torch.cuda.is_available()
# use_cuda = False

class Guesser(nn.Module):

    def __init__(self, hidden_encoder_dim, categories_length, cat2id, object_embedding_dim):
        """
        Parameters
        hidden_encoder_dim          Dimensionality of the hidden state of the encoder
        categories_length           Number of object categories_length
        cat2id                      Dictionary to convert a category into an id
        object_embedding_dim        Dimensionality of the object embeddings
        """
        super(Guesser, self).__init__()

        self.spatial_dim = 8
        self.categories_length = categories_length
        self.object_embedding_dim = object_embedding_dim
        self.hidden_encoder_dim = hidden_encoder_dim
        self.cat2id = cat2id

        self.object_embeddings = nn.Embedding(self.categories_length, self.object_embedding_dim)

        self.mlp_model = nn.Sequential(
            nn.Linear(self.object_embedding_dim + self.spatial_dim, 64),
            nn.ReLU(),
            nn.Linear(64, self.hidden_encoder_dim)
        )
        if use_cuda:
            self.mlp_model.cuda()

    def get_cat2id(self, cat):
        onehot = torch.zeros(self.categories_length)
        onehot[self.cat2id[cat]] = 1
        return onehot

    def img_spatial(self, img_meta):
        """ returns the spatial information of a bounding box """
        bboxes          = img_meta[0] # gets all bboxes in the image

        width           = img_meta[1]
        height          = img_meta[2]

        image_center_x  = width / 2
        image_center_y  = height / 2

        spatial = torch.FloatTensor(len(bboxes), 8)

        for i, bbox in enumerate(bboxes):
            x_min = bbox[0] / width
            y_min = bbox[1] / height
            x_max = (bbox[0] + bbox[2]) / width
            y_max = (bbox[1] + bbox[3]) / height

            w_box = x_max - x_min
            h_box = y_max - y_min

            x_min = x_min * 2 - 1
            y_min = y_min * 2 - 1
            x_max = x_max * 2 - 1
            y_max = y_max * 2 - 1

            x_center = (x_min + x_max) / 2
            y_center = (y_min + y_max) / 2

            spatial[i] = torch.FloatTensor([x_min, y_min, x_max, y_max, x_center, y_center, w_box, h_box])

        return spatial

    def forward(self, hidden_encoder, spatial, object_categories):

        # get the object embeddings of the objects in the image
        if use_cuda:
            obj_embeddings = self.object_embeddings(Variable(object_categories).cuda())
        else:
            obj_embeddings = self.object_embeddings(Variable(object_categories))

        # get the spatial info
        # spatial = self.img_spatial(img_meta)

        mlp_in = torch.cat([spatial, obj_embeddings], dim=1)

        proposed_embeddings = self.mlp_model(mlp_in)

        hidden_encoder = torch.cat([hidden_encoder[0]] * len(object_categories))

        return F.log_softmax(torch.mm(proposed_embeddings, hidden_encoder[0].view(self.hidden_encoder_dim, -1)).t())


import argparse
import networkx as nx

def read_fastq(path_fastq):
    """
    read fastq file and output a list of the reads
    """
    seq_read = []

    with open(path_fastq,'r') as filin:
        for ligne in filin:
            yield next(filin)
            next(filin)
            next(filin)

    return seq_read

def cut_kmer(seq,k_mer):
    """
    take a sequence and a k-mer size and return an iterator of k-mer
    """
    for i in range(len(seq)-k_mer):
        yield seq[i:(i+k_mer)]

def build_kmer_dict(path_fastq, k_mer):
    """
    take a fastq file in argument and a k mer size and return a dictionary with the
    k-mer as keys and the number of occurences as values
    """
    dict_k_mer = {}
    for seq_read in read_fastq(path_fastq):
        for iterator_k_mer in cut_kmer(seq_read, k_mer):
            if iterator_k_mer in dict_k_mer:
                dict_k_mer[iterator_k_mer] +=1
            else:
                dict_k_mer[iterator_k_mer] = 1
    print(dict_k_mer)

def build_graph(dict_k_mer):
    """
    creat a networkx graph
    """
    G=nx.Graph()

def main():
    """
    """
    parser = argparse.ArgumentParser()

    parser.add_argument("-i", nargs="?",\
        help="fichier fastq single end")
    parser.add_argument("-k", nargs="?",\
        help="taille des kmer (optionnel - default 21)",)
    parser.add_argument("-o", nargs="?",\
        help="fichier config")

    args = parser.parse_args()

    build_kmer_dict(args.i, 3)
if __name__ == "__main__":
    main()