import cmath
import numpy.ma
import sys
import matplotlib.pyplot as plt
from objs import *
import math
import copy
"""
This is the utility file for the calculator, nearly all features are kept and maintained here. it includes:
tokenizer(expression)
findNeg(token list)
fixmult(token list)
check(token list)
toReversePolish(token list)
getPrecedence(token data)
calculate(token list, value)
get_function(token)
graph(token_list, xmin,xmax)
isnum(number)
treeify(token list)
infix_traversal(postfix tree)
expression(token list)
collapse(token list)
list_to_string(token list)
derivative(postfix tree)
"""
def tokenizer(expression, lst = [],flag = True):
    """
    Recursive algorithm that takes a input string and converts it into a list of tokens, thus making processing easier.
    :param expression: Input string
    :param lst: keeps track of current list of tokens produced
    :param flag: Solution to scope issues do to default arguments being global, there may be a more elegant solution to
    be found later.
    :return: List of tokens
    """
    if lst !=[] and flag:
        lst = [SOF()]
    elif lst == []:
        lst = [SOF()]
    if expression[:1].isdigit() or expression[:1] == ".":
        i = 0
        while len(expression) !=i and (expression[i].isdigit() or expression[i] == "."):
            i += 1
        if i == 0:
            i = 1
        if len(expression) !=i:
            try:
                tok = num(float(expression[:i]))
            except ValueError:
                print(expression[:1] + " is not a number, quitting now...")
                sys.exit()
        else:
            try:
                tok = num(float(expression))
            except ValueError:
                print(expression + " is not a number, quitting now...")
                sys.exit()
        lst.append(tok)
        return tokenizer(expression[i:],lst,False)

    elif expression[:1] in ["+","-","*","/","^"]:
        tok = oper(expression[0])
        lst.append(tok)
        return tokenizer(expression[1:],lst,False)

    elif expression[:1] == "(":
        lst.append(leftparen())
        return tokenizer(expression[1:],lst,False)

    elif expression[:1] == ")":
        lst.append(rightparen())
        return tokenizer(expression[1:], lst,False)

    elif expression[:1] == 'x':
        lst.append(var())
        return tokenizer(expression[1:],lst,False)

    elif expression[:1].isalpha():
        i = 1
        while expression[i].isalpha():
            i += 1
        tok = expr(expression[:i])
        lst.append(tok)
        return tokenizer(expression[i:],lst,False)
    elif expression[:1] == "":
        lst.append(EOF())
        return lst
def findNeg(toks):
    """
    Due to my processing, a negative symbol looks the same as a minus sign. This converts any oper("-") into
    [leftparen(),num(-l),oper("*"),num(NUMBER TO BE MADE NEGATIVE),rightparen()] so long as another oper preceeds in and
    a number or var follows it.
    :param toks: Token list
    :return: Adjusted token list
    """
    lst = []
    for i in range(len(toks)):

        if type(toks[i]) is oper:
            if toks[i].data == "-" and ((type(toks[i-1]) is SOF or type(toks[i-1]) is oper or type(toks[i-1]) is leftparen) and (type(toks[i+1]) is num or type(toks[i+1]) is var)):
                lst.append(i)
    lst.sort(reverse=True)
    for j in lst:
        toks.insert(j+1,leftparen())
        toks.insert(j+2,num(-1))
        toks.insert(j+3,oper("*"))
        toks.insert(j+5,rightparen())
        toks.pop(j)

    return toks[1:]
def fixmult(toks):
    """
    Adjusts the token list so that all instance of "Nx" where N is a number, is replaced with N*x
    :param toks: token list
    :return: adjusted token list
    """
    lst =[]
    for i in range(len(toks)):
        if type(toks[i]) is num and (type(toks[i+1]) is var or type(toks[i+1]) is expr or type(toks[i+1]) is leftparen):
            lst.append(i)
    lst.sort(reverse=True)
    for i in lst:
        toks.insert(i+1,oper("*"))
    return toks
def check(toks):
    """
    Validates input to make sure it is mathematically valid, will throw an error if the input is invalid
    :param toks: token list
    :return: None
    """
    if not type(toks[-1]) is EOF:
        raise TypeError("EOF not at end.")
    for i in range(len(toks)):
        if type(toks[i]) is num:
            tempbool = type(toks[i+1]) is EOF or type(toks[i+1]) is rightparen or type(toks[i+1]) is oper
            if not tempbool:
                raise NameError("Invalid Syntax, num must be followed by EOF, right parenthesis, or an operation")
            if i!=0:
                tempbool = type(toks[i-1]) is oper or type(toks[i-1]) is leftparen
            if not tempbool:
                raise NameError("Invalid Syntax, num must be preceeded by operation or left parenthesis or Start of File")
        elif type(toks[i]) is oper:

            tempbool = type(toks[i+1]) is num or type(toks[1+i]) or type(toks[i+1]) is leftparen or type(toks[i+1]) is var
            if not tempbool:
                raise NameError("Invalid Syntax, operation must be followed by number, expression, or a variable")
            if i == 0:
                raise NameError("Synatax Error, cannot start expression with operation")
            else:
                tempbool = type(toks[i-1]) is num or type(toks[i-1]) is rightparen or type(toks[i-1]) is var
            if not tempbool:
                raise NameError("Invalid Syntax, operation must be preceeded by number, expression, or variable")
        elif type(toks[i]) is var:
            tempbool = type(toks[i + 1]) is EOF or type(toks[i + 1]) is rightparen or type(toks[i + 1]) is oper
            if not tempbool:
                raise NameError("Invalid Syntax, variable must be followed by EOF, right parenthesis, or an operation")
            if i != 0:
                tempbool = type(toks[i - 1]) is oper or type(toks[i-1]) is leftparen
            if not tempbool:
                raise NameError("Invalid Syntax, variable must be preceeded by operation or left parenthesis Or Start of File")
        elif type(toks[i]) is expr:
            tempbool = type(toks[i + 1]) is leftparen
            if not tempbool:
                raise NameError("Invalid Syntax, expression must be followed by left parenthesis")
            if i!=0:
                tempbool = type(toks[i-1]) is oper or type(toks[i-1]) is leftparen
            if not tempbool:
                raise NameError("Invalid syntax, expression preceeded by operation or left parenthesis or Start of File")
        elif type(toks[i]) is leftparen:
            k = -1
            j = i+1
            while k !=0:
                if type(toks[j]) is EOF:
                    raise RuntimeError("Reached end of expression while parsing")
                if type(toks[j]) is leftparen:
                    k -=1
                if type(toks[j]) is rightparen:
                    k +=1
                j+=1
            tempbool = type(toks[i+1]) is num or type(toks[i+1]) is expr or type(toks[i+1]) is var or type(toks[i+1]) is leftparen
            if not tempbool:
                raise NameError(" Invalid syntax, Left Parenthesis cannot be followed by an operation or End Of File")
            if i !=0:
                tempbool = type(toks[i-1]) is oper or type(toks[i-1]) is expr or type(toks[i-1]) is leftparen
            if not tempbool:
                raise NameError("Invalid syntax, Left Parenthesis can only be preceeded by an operation, expression, or Start of File")
        elif type(toks[i]) is rightparen:
            k = -1
            j = i -1
            while k != 0:
                if j ==len(toks):
                    raise RuntimeError("No left parenthesis to pair")
                if type(toks[j]) is leftparen:
                    k += 1
                if type(toks[j]) is rightparen:
                    k -= 1
                j-=1
            tempbool = type(toks[i + 1]) is EOF or type(toks[i + 1]) is rightparen or type(toks[i + 1]) is oper
            if not tempbool:
                raise NameError("Invalid Syntax, right parenthesis must be followed by EOF, right parenthesis, or an operation")
            if i != 0:
                tempbool = type(toks[i - 1]) is num or type(toks[i-1]) is var or type(toks[i-1]) is rightparen
            if not tempbool:
                raise NameError("Invalid syntax, Right Parenthesis can only be preceeded by an operation, expression, or Start of File")
    return toks
def toReversePolish(toks):
    """
    Uses the Shunting Yard algorithm to covert a infix list of tokens into a postfix string of tokens.
    :param toks: infix token list
    :return: postfix token list
    """
    Queue = queue()
    Stack = stack()
    lst = []
    for tok in toks:
        if type(tok) is num or type(tok) is var:
            Queue.enqueue(tok)
        elif type(tok) is expr:
            Stack.push(tok)
        elif type(tok) is oper:
            if not Stack.top is None:
                while type(Stack.top.data) is oper:
                    if (tok.data in ["-","+","*","/"] and getPrecedence(tok.data) <= getPrecedence(Stack.top.data.data)) or (tok.data == "^" and getPrecedence(tok.data) < getPrecedence(Stack.top.data.data)):
                        Queue.enqueue(Stack.top.data)
                        Stack.pop()
                        if Stack.is_empty():
                            break
                    else:
                        break
            Stack.push(tok)
        elif type(tok) is leftparen:
            Stack.push(tok)
        elif type(tok) is rightparen:
            if not Stack.is_empty():
                while not type(Stack.top.data) is leftparen:
                    Queue.enqueue(Stack.top.data)
                    Stack.pop()
                    if Stack.is_empty():
                        break
            Stack.pop()
            if not Stack.is_empty():
                if type(Stack.top.data) is expr:
                    Queue.enqueue(Stack.top.data)
                    Stack.pop()

        elif type(tok) is EOF:
            if not Stack.top is None:
                while type(Stack.top.data) is oper:
                    Queue.enqueue(Stack.top.data)
                    Stack.pop()
                    if Stack.top is None:
                        break
    while not Queue.is_empty():
        lst.append(Queue.front.data)
        Queue.dequeue()
    return lst
def getPrecedence(operator):
    """
    Returns the precedence for operators for use in toReversePolish(), where high numbers represent greater precedence
    :param operator: operator token data
    :return: number representing the precedence of the given operator
    """
    if operator == "^":
        return 3
    elif operator in ["*","/"]:
        return 2
    elif operator in ["+","-"]:
        return 1
def calculate(toks,val=0):
    """
    Calculates an inputted value of a postfix formatted list of tokens.
    :param toks: Postfix token list
    :param val: Input for token list
    :return: Result
    """
    for tok in toks:
        if type(tok) is var:
            tok.data = val
    my_stack = stack()
    for tok in toks:
        if type(tok) is num or type(tok) is var:
            my_stack.push(tok)
        elif type(tok) is oper:
            if tok.data == '+':
                num1 = my_stack.top.data.data
                my_stack.pop()
                num2 = my_stack.top.data.data
                my_stack.pop()
                my_stack.push(num(num1+num2))
            if tok.data == '-':
                num1 = my_stack.top.data.data
                my_stack.pop()
                num2 = my_stack.top.data.data
                my_stack.pop()
                my_stack.push(num(num2-num1))
            if tok.data == '*':
                num1 = my_stack.top.data.data
                my_stack.pop()
                num2 = my_stack.top.data.data
                my_stack.pop()
                my_stack.push(num(num1*num2))
            if tok.data == '/':
                num1 = my_stack.top.data.data
                my_stack.pop()
                num2 = my_stack.top.data.data
                my_stack.pop()
                try:
                    my_stack.push(num(num2/num1))
                except ZeroDivisionError:
                    return numpy.ma.masked
            if tok.data == '^':
                num1 = my_stack.top.data.data
                my_stack.pop()
                num2 = my_stack.top.data.data
                my_stack.pop()
                my_stack.push(num(num2**num1))
        elif type(tok) is expr:
            num1 = my_stack.top.data.data
            my_stack.pop()
            my_stack.push(num(get_function(tok,num1)))
    return my_stack.top.data.data
def get_function(tok,val):
    """
    Lookup and calculation function for different possible functions.
    :param tok: expr token
    :param val: input value to tok
    :return: result
    """
    if tok.data == "sin":
        if type(val) is float or type(val) is int:
            return round(math.sin(float(val)),4)
        elif type(val) is complex:
            return cmath.sin(val)
    if tok.data == "cos":
        if type(val) is float or type(val) is int:
            return round(math.cos(float(val)),4)
        elif type(val) is complex:
            return cmath.cos(val)
    if tok.data == "tan":
        if type(val) is float or type(val) is int:
            return round(math.tan(float(val)),4)
        elif type(val) is complex:
            return cmath.tan(val)
    if tok.data == "sec":
        if type(val) is float or type(val) is int:
            return round(1/(math.cos(float(val))),4)
        elif type(val) is complex:
            return 1/(cmath.cos(val))
    if tok.data == "csc":
        if type(val) is float or type(val) is int:
            return round(1/(math.sin(float(val))),4)
        elif type(val) is complex:
            return 1/(cmath.sin(val))
    if tok.data == "cot":
        if type(val) is float or type(val) is int:
            return round(1/(math.tan(float(val))),4)
        elif type(val) is complex:
            return 1/(cmath.tan(val))
    if tok.data == "arccos":
        if type(val) is float or type(val) is int:
            return round(math.acos(float(val)),4)
        elif type(val) is complex:
            return cmath.acos(val)
    if tok.data == "arcsin":
        if type(val) is float or type(val) is int:
            return round(math.asin(float(val)),4)
        elif type(val) is complex:
            return cmath.asin(val)
    if tok.data == "arctan":
        if type(val) is float or type(val) is int:
            return round(math.atan(float(val)),4)
        elif type(val) is complex:
            return cmath.atan(val)
    if tok.data == "ln":
        if (type(val) is float or type(val) is int) and val > 0:
            return round(math.log(float(val)),4)
        elif type(val) is complex or val <=0:
            return cmath.log(val)
    if tok.data == "sqrt":
        if (type(val) is float or type(val) is int) and val > 0:
            return round(math.sqrt(float(val)),4)
        elif type(val) is complex or val<=0:
            return cmath.sqrt(val)
    else:
        raise NameError("Function does not exist in lookup table, please add for functionality")

def graph(func,minimum,maximum):
    """
    Graphs a function represented by a postfix list of tokens
    :param func: Function to graph
    :param minimum: x minimum
    :param maximum: y maximum
    :return: None
    """
    count = minimum
    y = []
    while count <= maximum:
        y.append(count)
        count+=.001
    count = minimum
    x = []
    while count <= maximum:
        try:
            x.append(calculate(func,count))
        except ValueError or TypeError:
            x.append(numpy.ma.masked)
        count+=.001
    y = numpy.ma.array(y)
    x = numpy.ma.array(x)
    plt.plot(y,x)
    plt.axhline(0, color='black')
    plt.axvline(0, color='black')
    plt.show()
def isnum(num):
    """
    Returns true if the inputted argument can be converted into a float or int
    :param num:
    :return: Boolean value
    """
    try:
        float(num)
    except ValueError:
        return False
    return True

def treeify(toks,count = 0):
    """
    Turns a postfix list of tokens into a tree, POSTFIX LIST MUST BE REVERSED FOR PROCESSING
    :param toks: Token list to be turned into a tree
    :param count: Keeps track of where to make the next branch of the tree.
    :return: Postfix Tree
    """
    if type(toks[0]) is num or type(toks[0]) is var:
        return tree(toks[0]),count
    if type(toks[0]) is expr:
        temp = expr_tree(toks[0])
        child,count =treeify(toks[1:],count)
        temp.add(child)
        count+=1
        return temp,count
    temp = tree(toks[0])
    right,count = treeify(toks[1+count:])
    left,count = treeify(toks[2+count:])
    count+=2
    temp.add(right)
    temp.add(left)
    return temp,count
def infix_traversal(my_tree):
    """
    Performs an infix traversal of an postfix tree.
    :param my_tree: Postfix tree
    :return: Infix list of tokens
    """
    if type(my_tree) is tree:
        if my_tree.left is None:
            return expression([my_tree.node])
        temp = expression([infix_traversal(my_tree.left),my_tree.node,infix_traversal(my_tree.right)])
        temp1 = []
        for i in temp:
            if type(i) is list:
                temp1 += i
            else:
                temp1 +=[i]
        return temp1
    else:
        if my_tree.child is None:
            return
        temp = expression([my_tree.node,infix_traversal(my_tree.child)])
        return temp
def expression(lst):
    """
    Adds wrapping left and right parenthesis to a token list.
    :param lst: Input list
    :return: Wrapped list
    """
    temp = [leftparen()]
    temp1 = [rightparen()]
    return temp + lst + temp1
def collapse(lst):
    """
    Removes all sublists from a list while keeping their values
    :param lst: List to be collapsed.
    :return: List
    """
    temp = []
    for i in lst:
        if type(i) is list:
            temp +=collapse(i)
        else:
            temp +=[i]
    return temp
def list_to_string(lst):
    """
    Converts an infix token list into an infix string
    :param lst: token list
    :return: Infix string
    """
    temp = ""
    for i in lst:
        if type(i) is var:
            temp += "x"
        elif type(i) is num or type(i) is oper or type(i) is expr:
            temp += str(i.data)
        elif type(i) is leftparen:
            temp += "("
        elif type(i) is rightparen:
            temp += ")"
    return temp
def derivative(my_tree):
    """
    Recursive algorithm to calculate the analytical derivative of a function from a postfix tree. A simplifying algorithm
    still needs to be written to make the output more fit for human use, but is effective for the computational uses
    within this program. This feature is still in development, but it's mostly bug free.
    :param my_tree: Postfix tree
    :return: Infix list of tokens representing the derivative of the function.
    """
    if type(my_tree.node) is num:
        temp = num(0)
        return expression([temp])
    elif type(my_tree.node) is var:
        temp = num(1)
        return expression([temp])
    elif type(my_tree.node) is oper:
        if my_tree.node.data == "+":
            temp = [oper("+")]
            return expression([derivative(my_tree.left)] + temp +[derivative(my_tree.right)])
        elif my_tree.node.data == "-":
            temp = [oper("-")]
            return expression([derivative(my_tree.left)] + temp + [derivative(my_tree.right)])
        elif my_tree.node.data == "*":
            temp = [oper("*")]
            temp1 = [oper("+")]
            return expression(expression(expression([derivative(my_tree.left)]) + temp + expression([infix_traversal(my_tree.right)]))) + temp1 + expression(expression(expression([derivative(my_tree.right)]) + temp + expression([infix_traversal(my_tree.left)])))
        elif my_tree.node.data == "/":
            temp = [oper("*")]
            temp1 = [oper("-")]
            temp2 = [oper("/")]
            temp3 = [oper("^")] + [num(2)]
            return expression(expression(expression(expression([derivative(my_tree.left)]) + temp + expression([infix_traversal(my_tree.right)])) + temp1 + expression(expression([derivative(my_tree.right)]) + temp + expression([infix_traversal(my_tree.left)]))) + temp2 + expression([infix_traversal(my_tree.right)]) + temp3)
        elif my_tree.node.data == "^":
            temp = [oper("*")]
            temp1 = [oper("+")]
            temp2 = [oper("/")]
            return infix_traversal(my_tree) + temp + expression(expression(expression(expression([derivative(my_tree.left)])+ temp2 + expression(infix_traversal(my_tree.left))) + temp + infix_traversal(my_tree.right)) + temp1 + expression([expr("ln")] + infix_traversal(my_tree.left) + temp + [derivative(my_tree.right)]))
    elif type(my_tree.node) is expr:
        if my_tree.node.data == "sin":
            temp = [oper("*")]
            temp1 = [expr("cos")]
            return expression(expression([derivative(my_tree.child)]) + temp + expression(temp1 + expression([infix_traversal(my_tree.child)])) )
    if my_tree.node.data == "cos":
        temp = [oper("*")]
        temp1 = [num(-1),oper("*")]
        temp2 = [expr("sin")]
        return expression(temp1 + [derivative(my_tree.child)]) + temp + temp2 + expression(infix_traversal(my_tree.child))
    if my_tree.node.data == "tan":
        temp = [oper("*")]
        temp1 = [expr("sec")]
        temp2 = [oper("^")] + [num(2)]
        return expression([derivative(my_tree.child)]) + temp + expression(expression([temp1 + expression([infix_traversal(my_tree.child)])])+temp2)
    if my_tree.node.data == "sec":
        temp = [oper("*")]
        temp1 = [expr("sec")]
        temp2 = [expr("tan")]
        return expression([derivative(my_tree.child)]) + temp + expression(expression([temp1 + expression([infix_traversal(my_tree.child)])])+ temp +expression(temp2 + expression([infix_traversal(my_tree.child)])))
    if my_tree.node.data == "csc":
        temp = [oper("*")]
        temp1 = [expr("cot")]
        temp2 = [expr("csc")]
        temp3 = [num(-1),oper("*")]
        return expression([derivative(my_tree.child)]) + temp + expression(temp3 +expression([temp1 + expression([infix_traversal(my_tree.child)])])+ temp +expression(temp2 + expression([infix_traversal(my_tree.child)])))
    if my_tree.node.data == "cot":
        temp = [oper("*")]
        temp1 = [expr("csc")]
        temp2 = [oper("^")] + [num(2)]
        temp3 = [num(-1),oper("*")]
        return expression([derivative(my_tree.child)]) + temp + expression(temp3 +expression(expression([temp1 + expression([infix_traversal(my_tree.child)])])+temp2))
    if my_tree.node.data == "arcsin":
        temp = [oper("/")]
        temp1 = [expr("sqrt")]
        temp2 = [num(1),oper("-")]
        temp3 = [oper("^"),num(2)]
        return [derivative(my_tree.child)] + temp + expression(temp1 + expression(temp2 + expression([infix_traversal(my_tree.child)] + temp3)))
    if my_tree.node.data == "arccos":
        temp = [oper("/")]
        temp1 = [expr("sqrt")]
        temp2 = [num(1),oper("-")]
        temp3 = [oper("^"),num(2)]
        temp4 = [num(-1),oper("*")]
        return temp4 + expression([derivative(my_tree.child)] + temp + expression(temp1 + expression(temp2 + expression([infix_traversal(my_tree.child)] + temp3))))
    if my_tree.node.data == "arctan":
        temp = [oper("/")]
        temp1 = [num(1),oper("+")]
        temp2 = [oper("^"),num(2)]
        return [derivative(my_tree.child)] + temp + expression(temp1 + expression([infix_traversal(my_tree.child)] + temp2))
    if my_tree.node.data == "ln":
        temp = [oper("/")]
        return derivative(my_tree.child) + temp + infix_traversal(my_tree.child)
    if my_tree.node.data == "sqrt":
        temp = [num(.5),oper("*")]
        temp1 = [oper("*")]
        temp2 = [oper("^"),num("-.5")]
        return temp + expression(derivative(my_tree.child)) + temp1 + expression(infix_traversal(my_tree.child)+temp2)
def format_list(toks):
    """
    Coverts a list of tokens from derivative into a useable list for calculations
    :param toks: token list
    :return: Postfix formatted derivative
    """
    toks = collapse(toks)
    toks = list_to_string(toks)
    print(toks)
    toks = findNeg(tokenizer(toks))
    toks = toReversePolish(toks)
    return toks


from har2locust.plugin import entriesprocessor
import re
import pathlib

@entriesprocessor
def process(entries: list[dict]):
    headerignore_path = pathlib.Path(".headerignore")
    filters = []
    if headerignore_path.is_file():
        with open(headerignore_path) as f:
            filters = f.readlines()
            filters = [line.rstrip() for line in filters if line.strip() and not line.strip().startswith("#")]

    # always filter these, because they will be added by locust automatically
    filters.extend(["^cookie", "^content-length"])
    # always filter this, because it is not a real header
    filters.extend(["^:"])

    for e in entries:
        request = e["request"]
        request["headers"] = [h for h in request["headers"] if not any(re.search(r, h["name"]) for r in filters)]


import numpy as np
import warnings
from scipy import sparse
from ..utils import get_genomic_distances
from ..utils import get_intra_mask

warnings.warn(
    "The API of this module is likely to change. "
    "Use only for testing purposes")

def estimate_block_biases(counts, lengths, cnv, verbose=False):
    """
    Estimates block biases

    Parameters
    ----------
    counts : ndarray or sparse arrays (n, n)

    lengths : ndarray (L, )
        number of bins associated to each chromosomes.

    cnv : ndarray (n, )
        copy number associated to each bin. Only breakpoints are useful

    Returns
    -------
    block_biases : ndarray or sparse array (n, n)
        The estimated block biases
    """
    if sparse.issparse(counts):
        bias_es = np.ones(counts.data.shape)
        loci_sums = counts.sum(axis=0).A + counts.sum(axis=1).A
    else:
        bias_es = np.ones(counts.shape)
        loci_sums = (counts.sum(axis=0) + counts.sum(axis=1)) == 0
        counts[loci_sums] = np.nan
        counts[:, loci_sums] = np.nan

    print("")
    print("Estimating CNV-effects.")
    max_iter = 20
    min_iter = 5
    eps = 1e-1
    old_bias_es = None
    for it in range(max_iter):
        if verbose > 0:
            print("Iteration %d:" % it)

            print("1. Estimating mapping...")
        mapping = get_mapping(counts, lengths, bias_es, verbose=True)

        if verbose > 0:
            print("2. Estimating expected...")

        c_expected = get_expected(counts, lengths, bias_es, mapping=mapping)
        if np.any(c_expected < 0):
            raise ValueError("Found expected counts below 0.")

        if verbose > 0:
            print("3. Estimating CNV biases...")

        bias_es = estimate_bias(counts, cnv, c_expected, lengths,
                                mapping=mapping,
                                normalization_type="independant")

        if np.any(bias_es < 0):
            raise ValueError("Found a bias below 0.")

        if old_bias_es is not None:
            error = np.nanmax(np.abs(bias_es - old_bias_es))
        if it >= min_iter and old_bias_es is not None and error < eps:
            if verbose > 0:
                print("")
                print("Converged in %d iteration:" % it)
            break
        elif old_bias_es is not None:
            if verbose > 5:
                print("Error %0.2f" % error)

        old_bias_es = bias_es.copy()

    return bias_es

def normalize(counts, lengths, cnv, verbose=False):
    return remove_block_biases(counts, lengths, cnv, verbose=False)

def get_expected(counts, lengths, bs=None, mapping=None):

    if mapping is None:
        genomic_distances, expected_counts = get_mapping(counts, lengths, bs)
    else:
        if mapping.shape[0] == 2:
            genomic_distances, expected_counts = mapping
        else:
            genomic_distances, expected_counts = mapping.T

    gdis = get_genomic_distances(lengths, counts)
    if sparse.issparse(counts):
        c_expected = np.zeros(counts.data.shape)
    else:
        c_expected = np.zeros(counts.shape)

    for dis in np.unique(gdis):
        c_expected[gdis == dis] = expected_counts[genomic_distances == dis]
    return c_expected

def create_missing_distances_mask(counts, lengths):
    mask = (np.array(counts.sum(axis=0)).flatten() +
            np.array(counts.sum(axis=1)).flatten()) == 0
    missing_idx = np.where(mask)[0]
    n = counts.shape[0]

    row_idxs = np.concatenate(
        [np.tile(np.arange(n), len(missing_idx)),
         np.repeat(missing_idx, n)])
    col_idxs = np.concatenate(
        [np.repeat(missing_idx, n),
         np.tile(np.arange(n), len(missing_idx))])

    missing_distances_mask = sparse.coo_matrix(
        (np.ones(col_idxs.shape), (row_idxs, col_idxs)), shape=(n, n),
        dtype=np.bool)
    missing_distances_mask.sum_duplicates()
    return sparse.triu(missing_distances_mask, k=1)

def identify_missing_distances(counts, lengths):

    # Find rows that are completely empty. These correspond to filtered loci
    # and should be identified as missing distances
    mask = (np.array(counts.sum(axis=0)).flatten() +
            np.array(counts.sum(axis=1)).flatten()) == 0

    if not np.any(mask):
        gdis = np.arange(-1, lengths.max())
        return gdis, np.zeros(gdis.shape)

    chromosomes = np.array(
        [i for i, l in enumerate(lengths) for j in range(l)])
    l = np.array([i for i in lengths for j in range(i)])
    l_ = np.array(
        [lengths[:i].sum() for i, t in enumerate(lengths) for j in range(t)])
    l_[:lengths[0]] = 0
    missing_loci = np.where(mask)[0] - l_[mask]
    dis = np.concatenate(
        [missing_loci, l[mask] - missing_loci - 1]).flatten()
    dis = dis[dis > 0]
    gdis = np.arange(1, lengths.max() + 1)
    num = (gdis[:, np.newaxis] <= dis).sum(axis=1).astype(float)

    # Now remove distances counted twice
    c = chromosomes[mask]
    d = np.abs(missing_loci[:, np.newaxis] - missing_loci)
    d[c[:, np.newaxis] != c] = 0
    dis, count = np.unique(d, return_counts=True)
    count = count.astype(float)
    dis = dis.astype(int)
    count /= 2
    num[dis[dis != 0]-1] -= count[dis != 0]

    n = np.unique(chromosomes[mask], return_counts=True)[1]
    num_dup = np.triu(n[:, np.newaxis] * n, 1).sum()
    num = np.concatenate(
        [[(lengths.sum() - l[mask]).sum() - num_dup], [0], num])
    return np.arange(-1, lengths.max() + 1), num

def get_mapping(counts, lengths, bs=None, smoothed=True, verbose=False):
    if verbose:
        print("Computing relationship genomic distance & expected counts")

    if sparse.issparse(counts):
        gdis, means = _get_mapping_sparse(counts, lengths, bs)
    else:
        gdis, means = _get_mapping_dense(counts, lengths, bs)

    if not smoothed:
        return np.array([gdis, means])

    if verbose:
        print("Fitting Isotonic Regression")

    from sklearn.isotonic import IsotonicRegression
    ir = IsotonicRegression(increasing=False, out_of_bounds="clip")
    if gdis.min() > 0:
        y = np.array(means).flatten()
        x = np.arange(y.shape[0])
    elif gdis.min() == 0:
        y = np.array(means)[1:].flatten()
        x = np.arange(y.shape[0])
    else:
        y = np.array(means)[2:].flatten()
        x = np.arange(y.shape[0])

    mask = np.invert(np.isnan(y) | np.isinf(y) | (y == 0))
    ir.fit(
        x[mask],
        y[mask])
    means_fitted = ir.transform(x)
    if gdis.min() < 0:
        expected_counts = np.concatenate([[means[0], 0], means_fitted])
    elif gdis.min() == 0:
        expected_counts = np.concatenate([[0], means_fitted])
    else:
        expected_counts = means_fitted

    return np.array([gdis, expected_counts])

def _get_mapping_dense(counts, lengths, bs):
    if bs is None:
        bs = 1
    gdis = get_genomic_distances(lengths, counts)
    means = []

    for dis in np.arange(gdis.min(), gdis.max()+1):
        c = (counts / bs)[dis == gdis]
        means.append(np.nanmean(c))
    return np.arange(gdis.min(), gdis.max()+1), means

def _get_mapping_sparse(counts, lengths, bs):
    if bs is None:
        bs = 1
    gdis = get_genomic_distances(lengths, counts)
    means = []
    # Compute the number of missing rows/col for each chromose
    gdis_, num_ = identify_missing_distances(counts, lengths)

    for dis in np.arange(gdis.min(), max(lengths)):
        c = (counts.data / bs)[gdis == dis]
        if dis >= 0:
            t = lengths - dis
            num = (t[t > 0]).sum() - num_[gdis_ == dis]
        else:
            num = ((lengths).sum()**2 -
                   ((lengths)**2).sum()) / 2 - num_[gdis_ == dis]

        if num[0] < 0:
            raise ValueError("There cannot be less than 0 interactions")

        # XXX We have non nan elements that still should not be included in
        # the mean. How can we count those ?
        # means.append(c.sum() / num[0])
        if num[0] - np.isnan(c).sum() == 0:
            means.append(np.nan)
        else:
            means.append(np.nansum(c) / (num[0] - np.isnan(c).sum()))

    return np.arange(gdis.min(), max(lengths)), means

def estimate_bias(counts, cnv, c_expected, lengths, mapping=None,
                  normalization_type="independant"):
    """
    Estimating the bias matrix

    Parameters
    ----------
    counts : ndarray or sparse matrix
        n by n contact count matrix

    cnv : ndarray (n, )
        the copy number
    """
    if sparse.issparse(counts):
        return _estimate_bias_sparse(
            counts, cnv, c_expected, lengths,
            mapping, normalization_type=normalization_type)
    else:
        return _estimate_bias_dense(counts, cnv, c_expected, lengths,
                                    normalization_type=normalization_type)

def _estimate_bias_dense_joint(counts, cnv, c_expected, lengths):
    bias = np.ones(counts.shape)

    for cnv_i in np.unique(cnv):
        for cnv_j in np.unique(cnv):
            if cnv_i > cnv_j:
                continue
            mask = ((cnv == cnv_i)[:, np.newaxis] * (cnv == cnv_j) |
                    (cnv == cnv_j)[:, np.newaxis] * (cnv == cnv_i))

            c = counts[mask]
            m = np.invert(np.isnan(c))
            c = c[m]
            c_exp = c_expected[mask][m]
            bias[mask] = (c * c_exp).sum() / (c_exp**2).sum()
    return bias

def _estimate_bias_dense(counts, cnv, c_expected, lengths,
                         normalization_type="independant"):
    if normalization_type not in ["independant", "chrjoint", "joint"]:
        raise ValueError("unknown type of normalization")

    bias = np.ones(counts.shape)
    intra = get_intra_mask(lengths)

    # Start with the inter
    for cnv_i in np.unique(cnv):
        for cnv_j in np.unique(cnv):
            if cnv_i > cnv_j:
                continue
            mask = ((cnv == cnv_i)[:, np.newaxis] * (cnv == cnv_j) |
                    (cnv == cnv_j)[:, np.newaxis] * (cnv == cnv_i))

            c = counts[mask]
            c_intra = intra[mask]
            m = np.invert(np.isnan(c)) & np.invert(c_intra)
            c = c[m]
            c_exp = c_expected[mask][m]
            bias[mask] = (c * c_exp).sum() / (c_exp**2).sum()
    bias[intra] = 1

    # Now do the intra. For the intra, we have to do piece by piece (XXX check
    # this).
    if normalization_type == "chrjoint":
        begin, end = 0, 0
        for i, length in enumerate(lengths):
            end += length
            scnv = cnv[begin:end]
            bias[begin:end, begin:end] = _estimate_bias_dense_joint(
                counts[begin:end, begin:end], scnv,
                c_expected[begin:end, begin:end], np.array([length]))
            begin = end
    elif normalization_type == "joint":
        counts[np.invert(intra)] = np.nan
        bias_ = _estimate_bias_dense_joint(counts, cnv, c_expected, lengths)
        bias[intra] = bias_[intra]
    else:
        breakpoints = np.where((cnv[1:] - cnv[:-1]).astype(bool))[0] + 1
        breakpoints = np.array(list(breakpoints) + list(lengths.cumsum()))
        breakpoints = np.unique(breakpoints)
        breakpoints.sort()

        begin_i, end_i = 0, 0
        for i, length_i in enumerate(breakpoints):
            end_i = length_i
            begin_j, end_j = 0, 0
            for j, length_j in enumerate(breakpoints):
                end_j = length_j
                if np.any(intra[begin_i:end_i, begin_j:end_j]):
                    c = counts[begin_i:end_i, begin_j:end_j]
                    # Extracting "interesting" contact count, which are
                    # non-filtering values from inter regions.
                    m = (np.invert(np.isnan(c)) &
                         intra[begin_i:end_i, begin_j:end_j])
                    c_exp = c_expected[begin_i:end_i, begin_j:end_j][m]
                    c = c[m]

                    # Least square estimater between c and c_exp
                    b = (c * c_exp).sum() / (c_exp**2).sum()
                    bias[begin_i:end_i,
                         begin_j:end_j] = b
                begin_j = end_j
            begin_i = end_i
    return bias

def _num_each_gdis(rows, cols, lengths):
    """
    Compute the number of elements that are in each genomic distances for the
    rows and cols provided

    Returns
    -------
    gdis, counts
    """
    chr_id = np.array([i for i, l in enumerate(lengths) for _ in range(l)])

    # All possible gdis.
    gdis = np.arange(-1, lengths.max())
    num = np.zeros(gdis.shape)
    # We are going to loop over the rows
    for row in rows:
        # We need to consider only the upper-triangular matrix, which is where
        # cols are larger than rows (XXX check this)
        dis = row - cols[np.newaxis]
        m = dis <= 0
        dis = np.abs(dis)
        # Find out which ones are inter chromosomal
        dis[chr_id[row] != chr_id[cols][np.newaxis]] = -1
        un, val = np.unique(dis[m], return_counts=True)
        num[un+1] += val
    # Removing diag
    num[1] = 0
    return gdis[num != 0], num[num != 0]

def _estimate_bias_sparse(counts, cnv, c_expected, lengths, mapping,
                          normalization_type="independant"):
    if normalization_type != "independant":
        raise NotImplementedError
    gdis, mapping = mapping

    bias = np.ones(counts.data.shape)
    intra = get_intra_mask(lengths, counts=counts)

    # Identify rows that are completely missing from the data. These should
    # not be taken in account when estimating the bias.
    mask = (np.array(counts.sum(axis=0)).flatten() +
            np.array(counts.sum(axis=1)).flatten()) == 0
    mask_idx = np.where(mask)[0]

    missing_loci = (mask_idx[:, np.newaxis] <= lengths.cumsum()).sum(axis=0)
    missing_loci = np.concatenate(
        [[missing_loci[0]], missing_loci[1:] - missing_loci[:-1]])

    # Start with the inter
    for cnv_i in np.unique(cnv):
        for cnv_j in np.unique(cnv):
            idx = np.where(
                ((cnv[counts.row] == cnv_i) &
                 (cnv[counts.col] == cnv_j)) |
                ((cnv[counts.row] == cnv_j) &
                 (cnv[counts.col] == cnv_i)))[0]
            c_intra = intra[idx]
            if not np.any(np.invert(c_intra)):
                continue

            c = counts.data[idx][np.invert(c_intra)]
            c_exp = c_expected[idx][np.invert(c_intra)]

            # XXX need to take in account 0 in the denominator.
            rows = np.array(
                [i for i in np.where(cnv == cnv_i)[0] if i not in mask_idx])
            cols = np.array(
                [i for i in np.where(cnv == cnv_j)[0] if i not in mask_idx])
            gdis_, num = _num_each_gdis(rows, cols, lengths)

            # We are only interested in the inters here
            denominator = (num[gdis_ == -1] * (mapping[gdis == -1] ** 2)).sum()
            bias[idx] = (c * c_exp).sum() / denominator

    bias[intra] = 1

    # Now do the intra. For the intra, we have to do piece by piece (XXX check
    # this).
    breakpoints = np.where((cnv[1:] - cnv[:-1]).astype(bool))[0] + 1
    breakpoints = np.array(list(breakpoints) + list(lengths.cumsum()))
    breakpoints = np.unique(breakpoints)
    breakpoints.sort()

    begin_i = 0
    for i, end_i in enumerate(breakpoints):
        begin_j, end_j = 0, 0
        rows = np.array(
            [i for i in np.arange(begin_i, end_i) if i not in mask_idx])

        for j, end_j in enumerate(breakpoints):
            idx = np.where((counts.row >= begin_i) & (counts.row < end_i) &
                           (counts.col >= begin_j) & (counts.col < end_j))

            if np.any(intra[idx]):
                cols = np.array(
                    [i for i in np.arange(begin_j, end_j)
                     if i not in mask_idx])

                gdis_, num = _num_each_gdis(rows, cols, lengths)

                c_exp = c_expected[idx][intra[idx]]
                denominator = sum(
                    [(n * (mapping[gdis == g] ** 2)).sum()
                     for g, n in zip(gdis_, num) if (g not in [-1, 0])])
                num = (counts.data[idx][intra[idx]] * c_exp).sum()
                bias[idx] = num / denominator
            begin_j = end_j
        begin_i = end_i
    return bias


"""
Correspond au script exécutable du programme client. Utilise tous les 'Widgets' PyQt5 personnalisés de custom_widgets.
Choisir le mode (local ou serveur) avant d'exécuter.
"""
import json
import socket
import sys
from sys import argv

from client.custom_widgets import *

SERVER = "178.79.165.80"
LOCAL = 'localhost'
PORT = 4500
MODE = SERVER
print(argv)
if len(argv) >= 1 and argv[1] == "local":
    MODE = LOCAL
try:
    PORT = int(argv[2])
except (ValueError, IndexError):
    pass

class Server_handler(QObject):
    infos_server = pyqtSignal(str)

    @pyqtSlot()
    def handle_server(self):
        while True:
            msg_server = server.recv(1024).decode()
            self.infos_server.emit(msg_server)

class main_window(QMainWindow):
    start_comm = pyqtSignal()

    def __init__(self, server):

        QMainWindow.__init__(self)
        self.server = server
        self.table = None
        self.init_widget_home()
        self.functions_dict = {'init_table': self.init_table, 'new_game': self.new_game,
                               'update_table': self.update_table, 'end_game': self.end_game, 'action': self.action,
                               'name ok': self.widget_home.ask_if_ready, 'error name': self.widget_home.wrong_name,
                               'disconnect': self.disconnect}

        self.setObjectName("main_window")
        self.setWindowTitle("PSL Poker")
        self.setMinimumSize(QSize(300, 230))

        self.menu_bar = QMenuBar(self)
        self.menu_bar.setGeometry(QRect(0, 0, 900, 22))
        self.menu_bar.setObjectName("menu_bar")

        self.server_handler = Server_handler()
        self.server_handler_thread = QThread()
        self.server_handler.moveToThread(self.server_handler_thread)
        self.server_handler_thread.start()

        self.start_comm.connect(self.server_handler.handle_server)
        self.server_handler.infos_server.connect(self.server_to_functions)

        self.show()
        self.start_comm.emit()

    def init_widget_home(self):

        self.resize(430, 300)
        self.widget_home = widget_home(server=server)
        self.setCentralWidget(self.widget_home)

    def init_widget_game(self):
        self.resize(1000, 700)
        self.client_name = self.widget_home.client_name
        self.setWindowTitle(f"PSL Poker - {self.client_name}")
        self.widget_game = widget_game(server=server)
        self.setCentralWidget(self.widget_game)

    @pyqtSlot(str)
    def server_to_functions(self, msg_server):

        serv_inf = json.loads(msg_server)
        print(f"\nFrom server to client :\n")
        for key, value in serv_inf.items():
            print(f"{key} : {value}")
        self.functions_dict[serv_inf['flag']](serv_inf)

    def init_table(self, serv_inf):
        players, time_to_play = serv_inf['players_data'], 30
        for i in range(len(players)):
            p = players[i]
            players[i] = Player(p['name'], p['id'], p['stack'], p['is_player'])
        self.init_widget_game()
        self.table = Table(players=players, widget_game=self.widget_game, time_to_play=time_to_play)
        self.table.show_used_widgets()
        self.repaint()
        print('Table has been initiated')

    def new_game(self, serv_inf):
        players, table_cards, dealer_id = self.table.players, self.table.widget_table.table_cards, serv_inf['dealer_id']
        client_cards = serv_inf['client_cards']
        sb, bb = serv_inf['blinds']
        for player in players:
            player.widget_player.is_not_speaking_and_did_not_win()
            if player.id == serv_inf['speaker_id']:
                player.widget_player.is_speaking()
            player.is_folded = player.is_all_in = False
            player.set_ogb(0)
            player.show_dealer_button(player.id == dealer_id)
            if player.is_client:
                player.widget_player.p_0_cards.set_cards(card1=client_cards[0], card2=client_cards[1])
            player.show_cards(True)
            for blind in [sb, bb]:
                if player.id == blind['player_id']:
                    player.set_ogb(blind['on_going_bet'])
                    player.set_stack(blind['player_stack'])
        table_cards.new_game()
        print('New_game has been started')

    def update_table(self, serv_inf):
        infos_players = serv_inf['infos_players']
        for infos_player in infos_players:
            player = self.table.get_player_from_id(infos_player['player_id'])
            player.set_ogb(infos_player['on_going_bet'])
            player.set_stack(infos_player['player_stack'])
            if infos_player['is_folded']:
                if not player.is_folded:
                    player.folds()
            if infos_player['is_all_in']:
                if not player.is_all_in:
                    player.all_in()
            if player.id == serv_inf['speaker_id']:
                player.widget_player.is_speaking()
            else:
                player.widget_player.is_not_speaking_and_did_not_win()
        self.table.set_pot(serv_inf['pot'])
        self.table.widget_table.table_cards.set_cards(serv_inf['table_cards'])
        print('Table has been updated')

    def action(self, serv_inf):
        stack, ogb = self.table.client_player.stack, self.table.client_player.ogb
        amount_to_call = serv_inf['amount_to_call']
        self.table.widget_game.action(stack, ogb, amount_to_call)
        print('Action has initiated')

    def end_game(self, serv_inf):
        for player in self.table.players:
            player.widget_player.is_not_speaking_and_did_not_win()
        for winner_id in serv_inf['winners_id']:
            winner = self.table.get_player_from_id(winner_id)
            winner.widget_player.won()
        if serv_inf['show_cards']:
            self.widget_end_game = widget_end_game(serv_inf['cards'])
        self.table.set_pot(0)
        print('Game has ended')

    def disconnect(self, serv_inf):
        print('Player has been disconnected')
        app.quit()

    def resizeEvent(self, e):
        w = self.width()
        self.setFixedHeight(0.7 * w)

class Player:
    def __init__(self, name, id, stack, is_client=False):
        self.name = name
        self.ogb = 0
        self.stack = stack
        self.is_client = is_client
        self.is_folded = self.is_all_in = False
        self.id = id

    def set_attributes(self, widget_p, widget_f):
        self.widget_player = widget_p
        self.widget_front = widget_f
        self.widget_player.label_name.setText(self.name)
        self.set_stack(self.stack)
        if self.is_client:
            self.widget_player.p_0_cards.set_name(self.name)

    def show_dealer_button(self, value):
        if value:
            self.widget_front.label_dealer.show()
        else:
            self.widget_front.label_dealer.hide()

    def show_cards(self, value):
        if value:
            self.widget_player.show_cards()
        else:
            self.widget_player.hide_cards()

    def set_ogb(self, value):
        self.ogb = value
        if value > 0:
            self.widget_front.widget_ogb.label.setText(str(value))
            self.widget_front.widget_ogb.show()
        else:
            self.widget_front.widget_ogb.hide()

    def set_stack(self, value):
        self.stack = value
        self.widget_player.label_stack.setText(str(value))

    def folds(self):
        self.widget_player.hide_cards()
        self.is_folded = True

    def all_in(self):
        self.widget_player.is_all_in()
        self.is_all_in = True

class Table:
    def __init__(self, players, widget_game, time_to_play):
        self.players = players
        self.nb_players = len(self.players)
        self.time_to_play = time_to_play
        self.widget_game, self.widget_table = widget_game, widget_game.widget_table
        for player in self.players:
            if player.is_client:
                self.client_player = player
        self.base_id = self.client_player.id
        self.connect_players_to_widgets()

    def connect_players_to_widgets(self):
        """On donne ici le table_id à chaque joueur de la table, et on donne à chaque joueur ses attributs
        wp et wf (widget_player et widget_front)"""

        ind_client = self.players.index(self.client_player)
        self.players.sort(key=lambda x: x.id)
        self.players = self.players[ind_client:] + self.players[:ind_client]
        for ind in range(self.nb_players):
            self.players[ind].table_id = table_ids_order[ind]
            ordered_widgets = sorted(table_ids_order[:self.nb_players])
            widget_id = ordered_widgets[ind]
            self.players[ind].set_attributes(widget_p=self.widget_table.widget_players[widget_id],
                                             widget_f=self.widget_table.widget_fronts[widget_id])

    def show_used_widgets(self):
        for player in self.players:
            player.widget_front.show()
            player.widget_player.show()

    def get_player_from_id(self, id):
        for player in self.players:
            if player.id == id:
                return player

    def set_pot(self, value):
        if value > 0:
            self.widget_table.table_cards.widget_pot.label.setText(str(value))
            self.widget_table.table_cards.widget_pot.show()
        else:
            self.widget_table.table_cards.widget_pot.hide()

def send_server(dico, server):
    dico = json.dumps(dico)
    print('From client to server : ', dico)
    server.send(dico.encode())

table_ids_order = [0, 4, 3, 2, 5, 1, 6]

if __name__ == '__main__':
    server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    server.connect((MODE, PORT))
    app = QApplication(sys.argv)
    game_window = main_window(server)
    sys.exit(app.exec_())


from filecmp import cmp
from re import T
from typing import *
import collections
from functools import cmp_to_key

from gevent import iwait
from sqlalchemy import false

class Solution1:
    def minimumCost(self, cost: List[int]) -> int:
        sortedCost=sorted(cost,reverse=True)
        for i in range(0,len(cost)):
            if i%3==2:
                cost.remove(sortedCost[i])
        return sum(cost)

class Solution2:
    def numberOfArrays(self, differences: List[int], lower: int, upper: int) -> int:
        nums=[0]
        for diff in differences:
            nums.append(nums[-1]+diff)
        return max(0,(upper-lower)-(max(nums)-min(nums))+1)

class Node:
    def __init__(self,row,col,price,dist):
        self.row=row
        self.col=col
        self.price=price
        self.dist=dist

def cmpNode(Node1,Node2):
    if Node1.dist==Node2.dist and Node1.price==Node2.price and Node1.row==Node2.row:
        return Node1.col-Node2.col
    if Node1.dist==Node2.dist and Node1.price==Node2.price:
        return Node1.row-Node2.row
    if Node1.dist==Node2.dist:
        return Node1.price-Node2.price
    return Node1.dist-Node2.dist

class Solution3:
    def highestRankedKItems(self, grid: List[List[int]], pricing: List[int], start: List[int], k: int) -> List[List[int]]:
        dist=collections.defaultdict(lambda:float("inf")) # saves tuple->int
        visited=set() # saves tuple
        queue=collections.deque() # saves tuple
        queue.append((start[0],start[1]))
        dist[(start[0],start[1])]=0
        m,n=len(grid),len(grid[0])
        while queue:
            curr=queue.popleft()
            if curr not in visited:
                visited.add(curr)
                if curr[0]+1<m and (curr[0]+1,curr[1]) not in visited and grid[curr[0]+1][curr[1]]!=0:
                    queue.append((curr[0]+1,curr[1]))
                    dist[(curr[0]+1,curr[1])]=dist[curr[0],curr[1]]+1
                if curr[0]-1>=0 and (curr[0]-1,curr[1]) not in visited and grid[curr[0]-1][curr[1]]!=0:
                    queue.append((curr[0]-1,curr[1]))
                    dist[(curr[0]-1,curr[1])]=dist[curr[0],curr[1]]+1
                if curr[1]+1<n and (curr[0],curr[1]+1) not in visited and grid[curr[0]][curr[1]+1]!=0:
                    queue.append((curr[0],curr[1]+1))
                    dist[(curr[0],curr[1]+1)]=dist[curr[0],curr[1]]+1
                if curr[1]-1>=0 and (curr[0],curr[1]-1) not in visited and grid[curr[0]][curr[1]-1]!=0:
                    queue.append((curr[0],curr[1]-1))
                    dist[(curr[0],curr[1]-1)]=dist[curr[0],curr[1]]+1

        nodes=[]
        for i in range(len(grid)):
            for j in range(len(grid[i])):
                if grid[i][j]<=pricing[1] and grid[i][j]>=pricing[0] and dist[(i,j)]<float("inf"):
                    nodes.append(Node(i,j,grid[i][j],dist[(i,j)]))

        #nodes.sort(key=lambda n:[n.dist,n.price,n.row,n.col])
        nodes.sort(key=cmp_to_key(cmpNode))
        ret=[]

        for i in range(min(k,len(nodes))):
            node=nodes[i]
            ret.append([node.row,node.col])
        return ret

class Solution4:
    def numberOfWays(self, corridor: str) -> int:
        plantsGroup=[]
        metS=0
        countP=0
        plantMode=False
        if corridor.count("S")%2!=0:
            return 0
        for i in range(len(corridor)):
            if corridor[i]=="S":
                if plantMode:
                    plantMode=False
                    plantsGroup.append(countP+1)
                    countP=0
                    metS=1
                else:
                    metS+=1
                    if metS==2:
                        plantMode=True
            else: # met P
                if plantMode:
                    countP+=1

        ret=1
        for num in plantsGroup:
            ret*=num
        return ret%(1000000000+7)


from evdev import InputDevice, categorize, ecodes
import math
import threading

gamepad = InputDevice('/dev/input/event0')

ABS_Y = 0
ABS_X = 1
ABS_RY = 3
ABS_RX = 4
ABS_Z = 317
ABS_RZ = 318
BTN_TL = 2
BTN_TR = 5
BTN_SOUTH = 304
BTN_NORTH = 308
BTN_WEST = 307
BTN_EAST = 305
BTN_THUMBL = 310
BTN_THUMBR = 311
BTN_SELECT = 314
BTN_START = 315
BTN_TRIGGER_HAPPY1 = 16 #-1
BTN_TRIGGER_HAPPY2 = 16 #+1
BTN_TRIGGER_HAPPY3 = 17 #-1
BTN_TRIGGER_HAPPY4 = 17 #+1

def remap(x, in_min, in_max, out_min, out_max):
    return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;

class xboxcontroller(object):
    MAX_TRIG_VAL = math.pow(2, 8)
    MAX_JOY_VAL = math.pow(2, 15)

    def __init__(self):
        self.LeftJoystickY = 0
        self.LeftJoystickX = 0
        self.RightJoystickY = 0
        self.RightJoystickX = 0
        self.LeftTrigger = 0
        self.RightTrigger = 0
        self.LeftBumper = 0
        self.RightBumper = 0
        self.A = 0
        self.X = 0
        self.Y = 0
        self.B = 0
        self.LeftThumb = 0
        self.RightThumb = 0
        self.Back = 0
        self.Start = 0
        self.LeftDPad = 0
        self.RightDPad = 0
        self.UpDPad = 0
        self.DownDPad = 0

        self._monitor_thread = threading.Thread(target=self._monitor_controller, args=())
        self._monitor_thread.daemon = True
        self._monitor_thread.start()

    def read(self):  # return the buttons/triggers that you care about in this methode
        # x = self.LeftJoystickX
        # y = self.LeftJoystickY
        x = round(self.LeftJoystickX, 1)
        y = round(self.LeftJoystickY, 1)
        a = self.A
        b = self.Y  # b=1, x=2
        rb = self.RightBumper
        return [x, y, a, b, rb]

    def readall(self):
        return [
            round(self.LeftJoystickY, 1),
            round(self.LeftJoystickX, 1),
            round(self.RightJoystickY, 1),
            round(self.RightJoystickX, 1),
            self.LeftTrigger,
            self.RightTrigger,
            self.LeftBumper,
            self.RightBumper,
            self.A,
            self.X,
            self.Y,
            self.B,
            self.LeftThumb,
            self.RightThumb,
            self.Back,
            self.Start,
            self.LeftDPad,
            self.RightDPad,
            self.UpDPad,
            self.DownDPad
        ]

    def readallraw(self):
        return [
            self.LeftJoystickY,
            self.LeftJoystickX,
            self.RightJoystickY,
            self.RightJoystickX,
            self.LeftTrigger,
            self.RightTrigger,
            self.LeftBumper,
            self.RightBumper,
            self.A,
            self.X,
            self.Y,
            self.B,
            self.LeftThumb,
            self.RightThumb,
            self.Back,
            self.Start,
            self.LeftDPad,
            self.RightDPad,
            self.UpDPad,
            self.DownDPad
        ]

    def readneeded(self):
        return [
            round(float(self.LeftJoystickY), 1),
            round(float(self.LeftJoystickX), 1),
            #round(self.RightJoystickY, 1),
            #round(self.RightJoystickX, 1),
            #round(self.RightTrigger - self.LeftTrigger, 1)
        ]

    def _monitor_controller(self):
        while True:
            for event in gamepad.read_loop():
                #print(event)
                if event.code == ABS_Y and event.type == 3:  # x & Y Wurden Vertauscht? fixed
                    self.LeftJoystickY = remap(event.value, -32767, 32768, +1, -1) #/ XboxController.MAX_JOY_VAL  # normalize between -1 and 1
                elif event.code == ABS_X:  # s.o.
                    self.LeftJoystickX = remap(event.value, -32767, 32768, +1, -1) #/  #/ XboxController.MAX_JOY_VAL  # normalize between -1 and 1
                elif event.code == ABS_RY:
                    self.RightJoystickY = remap(event.value, -32767, 32768, +1, -1) #/ XboxController.MAX_JOY_VAL  # normalize between -1 and 1
                elif event.code == ABS_RX:
                    self.RightJoystickX = remap(event.value, -32767, 32768, +1, -1) #/ XboxController.MAX_JOY_VAL  # normalize between -1 and 1
                elif event.code == ABS_Z:
                    self.LeftTrigger = event.value # NORMALIZE FEHLT  # normalize between 0 and 1
                elif event.code == ABS_RZ:
                    self.RightTrigger = event.value # NORMALIZE FEHLT  # normalize between 0 and 1
                elif event.code == BTN_TL:
                    self.LeftBumper = event.value
                elif event.code == BTN_TR:
                    self.RightBumper = event.value
                elif event.code == BTN_SOUTH:
                    #print("pressed")
                    #print(event.value)
                    self.A = event.value
                elif event.code == BTN_NORTH:
                    self.Y = event.value
                elif event.code == BTN_WEST:
                    self.X = event.value
                elif event.code == BTN_EAST:
                    self.B = event.value
                elif event.code == BTN_THUMBL:
                    self.LeftThumb = event.value
                elif event.code == BTN_THUMBR:
                    self.RightThumb = event.value
                elif event.code == BTN_SELECT:
                    self.Back = event.value
                elif event.code == BTN_START:
                    self.Start = event.value
                #elif event.code == BTN_TRIGGER_HAPPY1:
                #    self.LeftDPad = event.state
                #elif event.code == BTN_TRIGGER_HAPPY2:
                #    self.RightDPad = event.state
                #elif event.code == BTN_TRIGGER_HAPPY3:
                #    self.UpDPad = event.state
                #elif event.code == BTN_TRIGGER_HAPPY4:
                #    self.DownDPad = event.state


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import glob
import ntpath
from setuptools import setup, find_packages

def get_module_name(module_path):
    """
    Return the module name of the module path
    """
    return ntpath.split(module_path)[1].split(".")[0]

def snake_to_camel(word):
    """
    Convert a word from snake_case to CamelCase
    """
    return ''.join(x.capitalize() or '_' for x in word.split('_'))

setup(
    name='fn_wiki',
    version='1.0.1',
    license='MIT',
    author='Resilient Labs',
    author_email='',
    url="http://ibm.biz/resilientcommunity",
    description="function which looks for search term in a wiki and returns the line it contains ",
    long_description="Perform operations against the Resilient wiki: create or update pages, read wiki contents and perform lookups of content.",
    install_requires=[
        'resilient_circuits>=30.0.0',
        'resilient_lib'
    ],
    packages=find_packages(),
    include_package_data=True,
    platforms='any',
    python_requires='>=3',
    classifiers=[
        'Programming Language :: Python',
        'Programming Language :: Python :: 3'
    ],
    entry_points={
        "resilient.circuits.components": [
            "{}FunctionComponent = fn_wiki.components.{}:FunctionComponent".format(snake_to_camel(get_module_name(filename)), get_module_name(filename)) for filename in glob.glob("./fn_wiki/components/[a-zA-Z]*.py")
        ],
        "resilient.circuits.configsection": ["gen_config = fn_wiki.util.config:config_section_data"],
        "resilient.circuits.customize": ["customize = fn_wiki.util.customize:customization_data"],
        "resilient.circuits.selftest": ["customize = fn_wiki.util.selftest:selftest_function"]
    }
)


from pathlib import Path, PurePath
import math
import re
import time
import uuid
from typing import Any, Tuple, List

from librespot.metadata import TrackId
import ffmpy

from zotify.const import TRACKS, ALBUM, GENRES, NAME, ITEMS, DISC_NUMBER, TRACK_NUMBER, IS_PLAYABLE, ARTISTS, IMAGES, URL, \
    RELEASE_DATE, ID, TRACKS_URL, FOLLOWED_ARTISTS_URL, SAVED_TRACKS_URL, TRACK_STATS_URL, CODEC_MAP, EXT_MAP, DURATION_MS, \
    HREF, ARTISTS, WIDTH
from zotify.termoutput import Printer, PrintChannel
from zotify.utils import fix_filename, set_audio_tags, set_music_thumbnail, create_download_directory, \
    get_directory_song_ids, add_to_directory_song_ids, get_previously_downloaded, add_to_archive, fmt_seconds
from zotify.zotify import Zotify
import traceback
from zotify.loader import Loader

def get_saved_tracks() -> list:
    """ Returns user's saved tracks """
    songs = []
    offset = 0
    limit = 50

    while True:
        resp = Zotify.invoke_url_with_params(
            SAVED_TRACKS_URL, limit=limit, offset=offset)
        offset += limit
        songs.extend(resp[ITEMS])
        if len(resp[ITEMS]) < limit:
            break

    return songs

def get_followed_artists() -> list:
    """ Returns user's followed artists """
    artists = []
    resp = Zotify.invoke_url(FOLLOWED_ARTISTS_URL)[1]
    for artist in resp[ARTISTS][ITEMS]:
        artists.append(artist[ID])

    return artists

def get_song_info(song_id) -> Tuple[List[str], List[Any], str, str, Any, Any, Any, Any, Any, Any, int]:
    """ Retrieves metadata for downloaded songs """
    with Loader(PrintChannel.PROGRESS_INFO, "Fetching track information..."):
        (raw, info) = Zotify.invoke_url(f'{TRACKS_URL}?ids={song_id}&market=from_token')

    if not TRACKS in info:
        raise ValueError(f'Invalid response from TRACKS_URL:\n{raw}')

    try:
        artists = []
        for data in info[TRACKS][0][ARTISTS]:
            artists.append(data[NAME])

        album_name = info[TRACKS][0][ALBUM][NAME]
        name = info[TRACKS][0][NAME]
        release_year = info[TRACKS][0][ALBUM][RELEASE_DATE].split('-')[0]
        disc_number = info[TRACKS][0][DISC_NUMBER]
        track_number = info[TRACKS][0][TRACK_NUMBER]
        scraped_song_id = info[TRACKS][0][ID]
        is_playable = info[TRACKS][0][IS_PLAYABLE]
        duration_ms = info[TRACKS][0][DURATION_MS]

        image = info[TRACKS][0][ALBUM][IMAGES][0]
        for i in info[TRACKS][0][ALBUM][IMAGES]:
            if i[WIDTH] > image[WIDTH]:
                image = i
        image_url = image[URL]

        return artists, info[TRACKS][0][ARTISTS], album_name, name, image_url, release_year, disc_number, track_number, scraped_song_id, is_playable, duration_ms
    except Exception as e:
        raise ValueError(f'Failed to parse TRACKS_URL response: {str(e)}\n{raw}')

def get_song_genres(rawartists: List[str], track_name: str) -> List[str]:
    if Zotify.CONFIG.get_save_genres():
        try:
            genres = []
            for data in rawartists:
                # query artist genres via href, which will be the api url
                with Loader(PrintChannel.PROGRESS_INFO, "Fetching artist information..."):
                    (raw, artistInfo) = Zotify.invoke_url(f'{data[HREF]}')
                if Zotify.CONFIG.get_all_genres() and len(artistInfo[GENRES]) > 0:
                    for genre in artistInfo[GENRES]:
                        genres.append(genre)
                elif len(artistInfo[GENRES]) > 0:
                    genres.append(artistInfo[GENRES][0])

            if len(genres) == 0:
                Printer.print(PrintChannel.WARNINGS, '###    No Genres found for song ' + track_name)
                genres.append('')

            return genres
        except Exception as e:
            raise ValueError(f'Failed to parse GENRES response: {str(e)}\n{raw}')
    else:
        return ['']

def get_song_lyrics(song_id: str, file_save: str) -> None:
    raw, lyrics = Zotify.invoke_url(f'https://spclient.wg.spotify.com/color-lyrics/v2/track/{song_id}')

    if lyrics:
        try:
            formatted_lyrics = lyrics['lyrics']['lines']
        except KeyError:
            raise ValueError(f'Failed to fetch lyrics: {song_id}')
        if(lyrics['lyrics']['syncType'] == "UNSYNCED"):
            with open(file_save, 'w+', encoding='utf-8') as file:
                for line in formatted_lyrics:
                    file.writelines(line['words'] + '\n')
            return
        elif(lyrics['lyrics']['syncType'] == "LINE_SYNCED"):
            with open(file_save, 'w+', encoding='utf-8') as file:
                for line in formatted_lyrics:
                    timestamp = int(line['startTimeMs'])
                    ts_minutes = str(math.floor(timestamp / 60000)).zfill(2)
                    ts_seconds = str(math.floor((timestamp % 60000) / 1000)).zfill(2)
                    ts_millis = str(math.floor(timestamp % 1000))[:2].zfill(2)
                    file.writelines(f'[{ts_minutes}:{ts_seconds}.{ts_millis}]' + line['words'] + '\n')
            return
    raise ValueError(f'Failed to fetch lyrics: {song_id}')

def get_song_duration(song_id: str) -> float:
    """ Retrieves duration of song in second as is on spotify """

    (raw, resp) = Zotify.invoke_url(f'{TRACK_STATS_URL}{song_id}')

    # get duration in miliseconds
    ms_duration = resp['duration_ms']
    # convert to seconds
    duration = float(ms_duration)/1000

    return duration

def download_track(mode: str, track_id: str, extra_keys=None, disable_progressbar=False) -> None:
    """ Downloads raw song audio from Spotify """

    if extra_keys is None:
        extra_keys = {}

    prepare_download_loader = Loader(PrintChannel.PROGRESS_INFO, "Preparing download...")
    prepare_download_loader.start()

    try:
        output_template = Zotify.CONFIG.get_output(mode)

        (artists, raw_artists, album_name, name, image_url, release_year, disc_number,
         track_number, scraped_song_id, is_playable, duration_ms) = get_song_info(track_id)

        song_name = fix_filename(artists[0]) + ' - ' + fix_filename(name)

        for k in extra_keys:
            output_template = output_template.replace("{"+k+"}", fix_filename(extra_keys[k]))

        ext = EXT_MAP.get(Zotify.CONFIG.get_download_format().lower())

        output_template = output_template.replace("{artist}", fix_filename(artists[0]))
        output_template = output_template.replace("{album}", fix_filename(album_name))
        output_template = output_template.replace("{song_name}", fix_filename(name))
        output_template = output_template.replace("{release_year}", fix_filename(release_year))
        output_template = output_template.replace("{disc_number}", fix_filename(disc_number))
        output_template = output_template.replace("{track_number}", fix_filename(track_number))
        output_template = output_template.replace("{id}", fix_filename(scraped_song_id))
        output_template = output_template.replace("{track_id}", fix_filename(track_id))
        output_template = output_template.replace("{ext}", ext)

        filename = PurePath(Zotify.CONFIG.get_root_path()).joinpath(output_template)
        filedir = PurePath(filename).parent

        filename_temp = filename
        if Zotify.CONFIG.get_temp_download_dir() != '':
            filename_temp = PurePath(Zotify.CONFIG.get_temp_download_dir()).joinpath(f'zotify_{str(uuid.uuid4())}_{track_id}.{ext}')

        check_name = Path(filename).is_file() and Path(filename).stat().st_size
        check_id = scraped_song_id in get_directory_song_ids(filedir)
        check_all_time = scraped_song_id in get_previously_downloaded()

        # a song with the same name is installed
        if not check_id and check_name:
            c = len([file for file in Path(filedir).iterdir() if re.search(f'^{filename}_', str(file))]) + 1

            fname = PurePath(PurePath(filename).name).parent
            ext = PurePath(PurePath(filename).name).suffix

            filename = PurePath(filedir).joinpath(f'{fname}_{c}{ext}')

    except Exception as e:
        Printer.print(PrintChannel.ERRORS, '###   SKIPPING SONG - FAILED TO QUERY METADATA   ###')
        Printer.print(PrintChannel.ERRORS, 'Track_ID: ' + str(track_id))
        for k in extra_keys:
            Printer.print(PrintChannel.ERRORS, k + ': ' + str(extra_keys[k]))
        Printer.print(PrintChannel.ERRORS, "\n")
        Printer.print(PrintChannel.ERRORS, str(e) + "\n")
        Printer.print(PrintChannel.ERRORS, "".join(traceback.TracebackException.from_exception(e).format()) + "\n")

    else:
        try:
            if not is_playable:
                prepare_download_loader.stop()
                Printer.print(PrintChannel.SKIPS, '\n###   SKIPPING: ' + song_name + ' (SONG IS UNAVAILABLE)   ###' + "\n")
            else:
                if check_id and check_name and Zotify.CONFIG.get_skip_existing():
                    prepare_download_loader.stop()
                    Printer.print(PrintChannel.SKIPS, '\n###   SKIPPING: ' + song_name + ' (SONG ALREADY EXISTS)   ###' + "\n")

                elif check_all_time and Zotify.CONFIG.get_skip_previously_downloaded():
                    prepare_download_loader.stop()
                    Printer.print(PrintChannel.SKIPS, '\n###   SKIPPING: ' + song_name + ' (SONG ALREADY DOWNLOADED ONCE)   ###' + "\n")

                else:
                    if track_id != scraped_song_id:
                        track_id = scraped_song_id
                    track = TrackId.from_base62(track_id)
                    stream = Zotify.get_content_stream(track, Zotify.DOWNLOAD_QUALITY)
                    create_download_directory(filedir)
                    total_size = stream.input_stream.size

                    prepare_download_loader.stop()

                    time_start = time.time()
                    downloaded = 0
                    with open(filename_temp, 'wb') as file, Printer.progress(
                            desc=song_name,
                            total=total_size,
                            unit='B',
                            unit_scale=True,
                            unit_divisor=1024,
                            disable=disable_progressbar
                    ) as p_bar:
                        b = 0
                        while b < 5:
                        #for _ in range(int(total_size / Zotify.CONFIG.get_chunk_size()) + 2):
                            data = stream.input_stream.stream().read(Zotify.CONFIG.get_chunk_size())
                            p_bar.update(file.write(data))
                            downloaded += len(data)
                            b += 1 if data == b'' else 0
                            if Zotify.CONFIG.get_download_real_time():
                                delta_real = time.time() - time_start
                                delta_want = (downloaded / total_size) * (duration_ms/1000)
                                if delta_want > delta_real:
                                    time.sleep(delta_want - delta_real)

                    time_downloaded = time.time()

                    genres = get_song_genres(raw_artists, name)

                    if(Zotify.CONFIG.get_download_lyrics()):
                        try:
                            get_song_lyrics(track_id, PurePath(str(filename)[:-3] + "lrc"))
                        except ValueError:
                            Printer.print(PrintChannel.SKIPS, f"###   Skipping lyrics for {song_name}: lyrics not available   ###")
                    convert_audio_format(filename_temp)
                    try:
                        set_audio_tags(filename_temp, artists, genres, name, album_name, release_year, disc_number, track_number)
                        set_music_thumbnail(filename_temp, image_url)
                    except Exception:
                        Printer.print(PrintChannel.ERRORS, "Unable to write metadata, ensure ffmpeg is installed and added to your PATH.")

                    if filename_temp != filename:
                        Path(filename_temp).rename(filename)

                    time_finished = time.time()

                    Printer.print(PrintChannel.DOWNLOADS, f'###   Downloaded "{song_name}" to "{Path(filename).relative_to(Zotify.CONFIG.get_root_path())}" in {fmt_seconds(time_downloaded - time_start)} (plus {fmt_seconds(time_finished - time_downloaded)} converting)   ###' + "\n")

                    # add song id to archive file
                    if Zotify.CONFIG.get_skip_previously_downloaded():
                        add_to_archive(scraped_song_id, PurePath(filename).name, artists[0], name)
                    # add song id to download directory's .song_ids file
                    if not check_id:
                        add_to_directory_song_ids(filedir, scraped_song_id, PurePath(filename).name, artists[0], name)

                    if not Zotify.CONFIG.get_bulk_wait_time():
                        time.sleep(Zotify.CONFIG.get_bulk_wait_time())
        except Exception as e:
            Printer.print(PrintChannel.ERRORS, '###   SKIPPING: ' + song_name + ' (GENERAL DOWNLOAD ERROR)   ###')
            Printer.print(PrintChannel.ERRORS, 'Track_ID: ' + str(track_id))
            for k in extra_keys:
                Printer.print(PrintChannel.ERRORS, k + ': ' + str(extra_keys[k]))
            Printer.print(PrintChannel.ERRORS, "\n")
            Printer.print(PrintChannel.ERRORS, str(e) + "\n")
            Printer.print(PrintChannel.ERRORS, "".join(traceback.TracebackException.from_exception(e).format()) + "\n")
            if Path(filename_temp).exists():
                Path(filename_temp).unlink()

    prepare_download_loader.stop()

def convert_audio_format(filename) -> None:
    """ Converts raw audio into playable file """
    temp_filename = f'{PurePath(filename).parent}.tmp'
    Path(filename).replace(temp_filename)

    download_format = Zotify.CONFIG.get_download_format().lower()
    file_codec = CODEC_MAP.get(download_format, 'copy')
    if file_codec != 'copy':
        bitrate = Zotify.CONFIG.get_transcode_bitrate()
        bitrates = {
            'auto': '320k' if Zotify.check_premium() else '160k',
            'normal': '96k',
            'high': '160k',
            'very_high': '320k'
        }
        bitrate = bitrates[Zotify.CONFIG.get_download_quality()]
    else:
        bitrate = None

    output_params = ['-c:a', file_codec]
    if bitrate:
        output_params += ['-b:a', bitrate]

    try:
        ff_m = ffmpy.FFmpeg(
            global_options=['-y', '-hide_banner', '-loglevel error'],
            inputs={temp_filename: None},
            outputs={filename: output_params}
        )
        with Loader(PrintChannel.PROGRESS_INFO, "Converting file..."):
            ff_m.run()

        if Path(temp_filename).exists():
            Path(temp_filename).unlink()

    except ffmpy.FFExecutableNotFoundError:
        Printer.print(PrintChannel.WARNINGS, f'###   SKIPPING {file_codec.upper()} CONVERSION - FFMPEG NOT FOUND   ###')


# código pra aula se encontra no Jupyter

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn import svm
from sklearn.metrics import plot_confusion_matrix
from sklearn.model_selection import train_test_split as tts
from sklearn.cluster import KMeans
from sklearn.preprocessing import MinMaxScaler

base = pd.read_excel("D:\GoogleDrive\Curso ASN.Rocks\SVM\programas\Bases\Case_Agencias.xlsx")

base.columns

perdas = base.columns[[x.startswith("perda") for x in base.columns]]
custos = base.columns[[x.startswith("custos") for x in base.columns]]
churn = base.columns[[x.startswith("churn") for x in base.columns]]
potRegiao = base.columns[[x.startswith("potencialRegiao") for x in base.columns]]
Over90 = base.columns[[x.startswith("Over90") for x in base.columns]]
PercFraudes = base.columns[[x.startswith("percFraudes") for x in base.columns]]

vars_invers_prop = base[perdas | custos | churn | potRegiao | Over90 | PercFraudes]
vars_diret_prop = base.drop(columns=perdas | custos | churn | potRegiao | Over90 | PercFraudes)

vars_invers_prop_scaled = MinMaxScaler().fit_transform(vars_invers_prop)
vars_diret_prop_scaled = MinMaxScaler().fit_transform(vars_diret_prop.iloc[:,1:271])

#Modeling
kmeans = KMeans(5)
kfit = kmeans.fit(vars_invers_prop_scaled)
base_invers_prop_cluster = kfit.predict(vars_invers_prop_scaled)
kfit.cluster_centers_
unique, counts = np.unique(kfit.labels_, return_counts=True)
# cluster 3 é o melhor
kmeans = KMeans(5)
kfit2 = kmeans.fit(vars_diret_prop_scaled)
base_diret_prop_cluster = kfit2.predict(vars_diret_prop_scaled)
kfit2.cluster_centers_
unique, counts = np.unique(kfit2.labels_, return_counts=True)
# cluster 0 é o melhor

base['Cl_invers'] = base_invers_prop_cluster
base['Cl_diret'] = base_diret_prop_cluster
base['target'] = np.zeros(len(base['Cl_invers']))
for i in range(len(base['Cl_invers'])):
    if ( (base['Cl_invers'][i]==3 or base['Cl_invers'][i]==4 ) and (base['Cl_diret'][i]==0 or base['Cl_diret'][i]==1) ):
        base.loc[i, 'target'] = 1
    else:
        base.loc[i, 'target'] = 0

base['target'].value_counts()

# SVM
X = base.drop(columns=['id', 'Cl_invers', 'Cl_diret', 'target'])
X_norm = MinMaxScaler().fit_transform(X)
Y = base['target']

# X_train, X_test, Y_train, Y_test = tts(X_norm, Y, test_size=0.2)

classificador = svm.SVC()
modelo_ajustado = classificador.fit(X_norm, Y)
predito = classificador.predict(X_norm)

plot_confusion_matrix(classificador, X_norm, Y, values_format='d', display_labels=["demais", "TOP"])


import os
import sys
import skimage

from django.conf import settings

import tensorflow as tf
from tensorflow.python.keras.backend import set_session

from .mrcnn.config import Config
from .mrcnn.model import MaskRCNN
from .mrcnn.visualize import display_instances

session = tf.compat.v1.Session()
graph = tf.compat.v1.get_default_graph()
set_session(session)

# Define the configuration for the model
class ModelConfig(Config):
    NAME = "banana_diseases_coco"
    # number of classes (background + number of disease categories)
    NUM_CLASSES = 1 + 4
    # number of training steps per epoch !TEST THIS
    STEPS_PER_EPOCH = 1
    USE_MINI_MASK = False
    GPU_COUNT = 1
    IMAGES_PER_GPU = 1
    DETECTION_MIN_CONFIDENCE = 0.6 # Skip detections with < 80% confidence
    CLASS_NAMES = ['BG', 'Black Sigatoka', 'Healthy', 'Yellow Sigatoka', 'Panama']

class MaskRCNNModel:
    """
        Wrapper class for the Mask R-CNN model.
    """

    def __init__(self):
        """
        Initializes the Mask R-CNN model and loads the pre-trained weights.
        """
        ROOT_DIR = os.path.abspath("./")
        sys.path.append(ROOT_DIR)
        self.config = ModelConfig()
        self.model = MaskRCNN(mode='inference', model_dir='logs', config=self.config)

        trained_model_path = os.path.join(settings.BASE_DIR, 'api/utils/disease_prediction_mrcnn/saved_models',
                                          f'mask_rcnn_{self.config.NAME}.h5')
        self.model.load_weights(trained_model_path, by_name=True)

        self.class_names = self.config.CLASS_NAMES

    def predict(self, img_path, show_box=False, verbose=False):
        """
        Runs object detection on the specified image and returns the predicted class names.

        Parameters:
        img_path (str): path to the input image

        Returns:
        pred_to_text (list): predicted class names of the detected objects
        """
        # Load the input image
        sample_img = skimage.io.imread(img_path)
        # plt.imshow(sample_img)

        global session
        global graph
        with graph.as_default():
            set_session(session)
            # Perform object detection on the input image
            detected = self.model.detect([sample_img])
            results = detected[0]

            # Display the results of the object detection
            display_instances(
                sample_img,
                results['rois'],
                results['masks'],
                results['class_ids'],
                self.class_names,
                results['scores'],
                "",  # title
                (16, 16),  # figsize
                None,  # ax
                True,  # show_mask=
                True,  # show_mask_polygon
                show_box,  # show_bbox
                None,  # colors
                None,  # captions
                True,  # show_caption
                img_path,  # save the plot
                None,  # filter_classes
                None  # min_score
            )

        # get area of each ROI
        class_scores_areas = {}

        # Iterate over the class IDs, scores, and masks simultaneously
        for class_id, score, mask in zip(results['class_ids'], results['scores'],
                                         results['masks'].transpose(2, 0, 1)):
            # Check if the class ID is already present in the dictionary
            if class_id not in class_scores_areas:
                # If not, initialize an empty list for that class ID
                class_scores_areas[class_id] = []

            # Calculate the area of the mask
            area = mask.sum()

            # Append the score and area to the class's list
            class_scores_areas[class_id].append({'score': score, 'area': area})

        # Convert numerical class IDs to labels
        class_scores_areas_with_labels = {}
        for class_id, scores_areas in class_scores_areas.items():
            class_name = self.class_names[class_id]
            class_scores_areas_with_labels[class_name] = scores_areas

        if verbose:
            print(f'Detected Results:\n\t{class_scores_areas_with_labels}')

        return class_scores_areas_with_labels

# model = MaskRCNNModel()
# pred_to_text = model.predict('bb.jpeg')
# print(pred_to_text)


#!/bin/python

import json
import collections as c

import sys

import numpy as np
from PIL import Image
from wordcloud import WordCloud

def red_color_func(word, font_size, position, orientation, random_state=None, **kwargs):
    return "#FF0000"

# if no file is given, print usage and exit
if len(sys.argv) < 2:
    print("Usage: wordcloud <path_to_json>")
    exit()

try:
    data = json.load( open(sys.argv[1], 'r') )
except FileNotFoundError:
    print("Couldn't find file. Make sure to give the whole path like <directory>/<file>.json")
    quit()
except json.decoder.JSONDecodeError:
    print("Couldn't decode the JSON. It may be corrupted. Make sure you use the unaltered .json-file from google takeouts.")
    quit()
except Error:
    print("An unknown Error occured, please contact the project maintainer")
    quit()

data = filter(lambda a: 'subtitles' in list(a.keys()) and a['time'].startswith('2020'), data)
yters = c.Counter( [a['subtitles'][0]['name'] for a in data] )

mask = np.array( Image.open("yt_logo.png") )

word_cloud = WordCloud( background_color="#282828"
    , repeat=False
    , relative_scaling=.75
    , margin=4
    , font_step=5
    , random_state=1
    , max_words=600
    , mask=mask
    ).generate_from_frequencies(yters)

word_cloud.recolor(color_func=red_color_func)
word_cloud.to_file("wordcloud.png")


from rest_framework import serializers

from mainApp.models import category, customCategory, product

class getProductsSerializer(serializers.ModelSerializer):
    class Meta:
        model = product
        fields = '__all__'

class getSubCategoriesSerializer(serializers.ModelSerializer):
    # subs = getProductsSerializer(many=True)
    class Meta:
        model = customCategory
        fields = ('id','title','image','category','subs')

class getCustomCategoriesSerializer(serializers.ModelSerializer):
    subs = getSubCategoriesSerializer(many=True)

    class Meta:
        model = category
        fields = ('id',
                  'title',
                  'description',
                  'image',
                  'type',
                  'subs')

# class getStCategoriesSerializer(serializers.ModelSerializer):
#     subs = getSubCategoriesSerializer(many=True)
#
#     class Meta:
#         model = category
#         fields = ('id',
#                   'title',
#                   'description',
#                   'image',
#                   'type',
#                   'subs')