import pytest, os
import warnings, collections, operator, logging
import generic_utils

## see https://docs.pytest.org/en/latest/example/simple.html#incremental-testing-test-steps
from typing import Dict, Tuple

# store history of failures per test class name and per index in parametrize (if parametrize used)
##_test_failed_incremental: Dict[str, Dict[Tuple[int, ...], str]] = {}
_test_failed_incremental = {}

RAISE_NO_CALLBACK_EXCEPTION = False

NT__scope = collections.namedtuple( 'scope', ['overview','identifier','description','priority','traceability'] )
NT__test_case_spec = collections.namedtuple( 'test_case_spec', ['overview', 'identifier', 'objective', 'priority', 'traceability', 'preconditions', 'inputs', 'expected_results'])
NT__test_case_spec = collections.namedtuple( 'test_case_spec', ['ov', 'id', 'obj', 'p', 'tr', 'prec', 'i', 'expected'])
NT__test_case = collections.namedtuple( 'test_case', ['spec', 'actual_results'])
NT__config = collections.namedtuple( 'config', ['project','scope','log_name'])

__overview__ = "Testing the local utilities module"
__identifier__ = "scope01"
__description__ = """Test of local utilities module, local_utilities.py, which contains functions and classes used in other modules"""
__priority__ = "MUST"
__traceability__ = "documentation is tbd"
test_scope = NT__scope( overview = __overview__, priority=__priority__,identifier=__identifier__, description=__description__, traceability=__traceability__)

project = 'rt001'
scope_id = 'sc001'

class BaseClassCheck(object):
    config = NT__config( project='default_project', scope='default_scope', log_name='test01' )
    reporter = None
    review_template = 'Failed tests: %s, passed tests: %s'

    @classmethod
    def configure(cls,project,scope,log_name,reporter=None):
        cls.config = NT__config( project=project, scope=scope, log_name=log_name )
        if reporter != None:
          cls.reporter = reporter

class Check3(BaseClassCheck):
    def __init__(self,func,sfx='',reset=False,blocking=False):
        self.ee = dict()
        self.function = func
        fid = func.__name__
        self.test_name = fid
        tr = '%s:%s:%s' % (self.config.project,self.config.scope,fid)
        defaults = {'prec':'None', 'i':'None', 'tr':tr, 'id':fid }
        ret = func.__annotations__['return']
        if BaseClassCheck.reporter == None:
            BaseClassCheck.reporter = LogReporter(self.config.log_name,sfx=sfx)

        for k,v in defaults.items():
            if k not in ret:
                ret[k] = v
        func.__annotations__['tc'] = TCBuild( func, BaseClassCheck.reporter, ret=ret )
        if blocking:
            func.__annotations__['tc'].is_blocking = True

        for k in ['ov','obj','p','tr','prec','i','expected','id']:
            v = ret.get(k, defaults.get(k) )
            self.ee[k] = v

        if reset:
            self._reset()
      ####  print ('FAIL COUNT: %s' % fid, self.reporter.fails )
      #### Note that the mark.incremental approach was tried,  but did not work with hooks implemented
        if self.reporter.blocking_fails > 0 and fid.find( 'wrapup' ) == -1:
            pytest.skip( 'blocking_fail count > 0' )

    @classmethod
    def _reset(cls):
         BaseClassCheck.reporter.fails = 0
         BaseClassCheck.reporter.passes = 0

    @classmethod
    def review(cls):
         return BaseClassCheck.review_template % (BaseClassCheck.reporter.fails,BaseClassCheck.reporter.passes)

    def __call__(self,value,cmt=None):
        self.function.__annotations__['tc'].result = (value,cmt)
        assert value == self.ee['expected'], '%s: result [%s] does not match expected [%s]' % (self.test_name,value,self.ee['expected'])

class NoCallback(Exception):
   def __init__(self,item,rep):
       self.item = item
       self.rep = rep

class BaseClassISOReports(object):
    pass

class BaseClassTS(object):
    pass

class LogReporter(BaseClassISOReports):
    def __init__(self,log_name,log_file=None,log_dir='./logs',sfx=None):
        self.log_name = log_name
        if log_file == None:
            log_file = "%s_2020" % log_name

        log_factory = generic_utils.LogFactory(dir=log_dir)
        log_pytest  = log_factory( log_name, mode="a", logfile=log_file )
        self.sfx = sfx
        self.fails = 0
        self.blocking_fails = 0
        self.passes = 0

    def __call__(self, pytest_report=None,test_case=None,sfx=None, cls=None):
        cmt = None
        if pytest_report != None and hasattr( pytest_report, 'outcome' ) and pytest_report.outcome in ['passed', 'failed', 'skipped']:
            tag = {'passed':'OK', 'failed':'FAIL', 'skipped':'SKIP'}[pytest_report.outcome]
        ##if pytest_report != None and hasattr( pytest_report, 'failed' ) and pytest_report.failed in [True,False]:
           ##tag = {True:'OK', False:'ERROR'}[not pytest_report.failed]
        else:
           tag = '-'

        if cls != None:
            cid = '%s' % cls.id
        else:
            cid = ''

        ##mode = "a" if os.path.exists("pytest_line_log") else "w"
        ##with open("pytest_line_log", mode) as f:
        if test_case == None:
                self.report_line =  '%s:%s..: ' % (tag, cid)
        else:
                self.report_line =  '%s:%s.%s: %s' % (tag, cid, test_case.spec.id, test_case.spec.ov)
                if hasattr( test_case, 'result' ):
                    if type( test_case.result ) == type( () ) and len( test_case.result ) == 2:
                        result, cmt  = [str( x) for x in test_case.result]
                    else:
                        result = str( test_case.result )
                else:
                    result = "****"
                cmt_item = cmt if cmt != None else ''

                if tag == 'FAIL':
                  self.fails += 1
                  if test_case.is_blocking:
                     self.blocking_fails += 1
                elif tag == 'OK':
                  self.passes += 1
                self.report_line = '\t'.join( [tag] + [str( test_case.spec._asdict()[x] ) for x in ['id','expected']] + [result,cmt_item] )

        if sfx != None:
                self.report_line += sfx
        if self.sfx != None:
            self.report_line += ' ' + self.sfx

        log = logging.getLogger( self.log_name )
        log.info( self.report_line )
            ## f.write( '%s\n' %  self.report_line  )
 ##           f.write( ', '.join( dir(item.cls) ) )
        return self.report_line

##line_reporter = LogReporter()

def get_user_warning(data):
    class HERE(UserWarning):
        pass
    setattr( HERE, 'data', data )
    return HERE

def matching( result, expected):
    """Check result against expected.
       Allows for encoding of lt/gt/le/ge in text strings.
       """
    if type(expected) == type('') and expected.find(':') != -1 and expected.split( ':' )[0] in ['lt','gt','le','ge']:
        exp = eval( expected[3:] )
        op = expected[:2]
        assert op in ['lt','gt','le','ge']
        return operator.__dict__[ op ](result,exp)
    else:
        return result == expected

class TCBuild(BaseClassTS):
    """
    BaseClassTS: brings in a standard report function which assumes a spec attribute which is an instance of NT__scope
    """
    scope = test_scope
    def __init__(self, function, reporter, ret=None):
      if ret == None:
        ret = function.__annotations__['return']
      self.reporter = reporter

      if type(ret) == type(dict()):
        self.spec = NT__test_case_spec( **ret )
      elif isinstance(ret,NT__test_case_spec):
        self.spec = ret
      print( 'TCBuild Instantiating: %s' % function.__name__ )

      self.function = function
      expected = self.spec.expected
      if type(expected) == type('') and expected.find( 'regex:' ) == 0:
         self.conformance_mode = 'match'
      else:
         self.conformance_mode = 'equals'

      self.ov = self.spec.ov
      self.is_blocking = False

    def report(self,*args,**kwargs):
        return self.reporter( *args,**kwargs)

    def __call__(self,*args,**kwargs):
        self.result = self.function(*args,**kwargs)
        return self.result

class MakeTest(object):
  """A decorator which converts a function into a pytest compatible test by appending an assertion checking the
    return value against an expected value.
    The expected value must be provided in the 'return' annotation of the function as part of a named tuple.
  """
  def __init__(self,reporter=None,log_file=None,log_name=None):
    if reporter != None:
        self.reporter = reporter
    elif BaseClassCheck.reporter != None:
        self.reporter = BaseClassCheck.reporter
    else:
        if log_name == None:
            log_name = __name__
        self.reporter = LogReporter(log_name,log_file=log_file)

  def __call__(self,f):
    expected = f.__annotations__['return']['expected']
    def this(*args,**kwargs) -> TCBuild(f,self.reporter):
        this.__annotations__['return'].result = result = f(*args,**kwargs)
        assert matching(result, expected), 'Result [%s] does not match expected [%s]' % (result,expected)

    return this

@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_makereport(item, call):

    # we only look at actual failing test calls, not setup/teardown

    # execute all other hooks to obtain the report object

    outcome = yield
    rep = outcome.get_result()

    if rep.when == "call":
           lr = None
           if any( [x in item.function.__annotations__.keys() for x in ['return','tc']] ) or 'tc' in item.funcargs:

                if 'tc' in item.function.__annotations__.keys():
                  src = 'tc'
                  ret = item.function.__annotations__['tc']
                elif 'return' in item.function.__annotations__.keys():
                  ret = item.function.__annotations__['return']
                  src = 'return'
                else:
                  src = 'tc*'
                  ret = item.funcargs['tc']

                ##
                ##objective here is to compile an ISO ... record combining information about tests and results.
                ##
                if isinstance( ret, BaseClassTS ) and  hasattr( ret, 'spec' ):
                    if hasattr( item, 'cls' ):
                        cls = item.cls
                    else:
                        cls = None
                    lr = ret.report( pytest_report=rep, test_case=ret, cls=cls )
                elif not isinstance( ret, BaseClassTS ):
                   warnings.warn("Test function ret is not a BaseClassTS instance: %s" % item.function.__name__, get_user_warning((item,rep) ) )
                else:
                   warnings.warn("Test function lacks specification: %s" % item.function.__name__, get_user_warning((item,rep) ) )
           else:
               warnings.warn("Test function lacks hook: %s" % item.function.__name__, get_user_warning((item,rep) ) )

           if lr == None:
               if RAISE_NO_CALLBACK_EXCEPTION:
                 raise NoCallback(item,rep)
               ##lr = line_reporter( pytest_report=rep, sfx='{no spec: %s}' % item.function.__name__ )


def main():
    while True:
        print("Meniu:")
        print("1. Rezolvare expresii matematice")
        print("2. Evaluare expresii booleane")
        print("3. Verificare tip și valoare expresii")
        print("4. Calcul valoare variabilă întreagă z")
        print("5. Comparare și explicație expresii")
        print("6. Anticipare și verificare expresii")
        print("7. Afișare variabile x și y")
        print("8. Calculul lui z cu formula dată")
        print("9. Evaluează expresia f(x)")
        print("10. Verifică dacă 4 numere sunt distincte")
        print("11. Găsește numărul egal cu suma celorlalte două")
        print("12.Individual")
        print("0. Ieșire")

        optiune = input("Selectați o opțiune: ")

        if optiune == "1":
            sarcina_1()
        elif optiune == "2":
            sarcina_2()
        elif optiune == "3":
            sarcina_3()
        elif optiune == "4":
            sarcina_4()
        elif optiune == "5":
            sarcina_5()
        elif optiune == "6":
            sarcina_6()
        elif optiune == "7":
            sarcina_7()
        elif optiune == "8":
            sarcina_8()
        elif optiune == "9":
            sarcina_9()
        elif optiune == "10":
            sarcina_10()
        elif optiune == "11":
            sarcina_11()
        elif optiune == "12":
            individual()
        elif optiune == "0":
            break
        else:
            print("Opțiune invalidă. Reîncercați.")

def sarcina_1():
    # Sarcina 1
    print("Rezultate pentru Sarcina 1:")
    print("a) 2/5 =", 2/5)
    print("b) 7.9+1.1 =", 7.9 + 1.1)
    print("c) 6-2 =", 6 - 2)
    print("d) 8+1 =", 8 + 1)
    print("e) 9+1.1 =", 9 + 1.1)
    print("f) 2*7 =", 2 * 7)
    print("g) 3.2/6 =", 3.2 / 6)
    print("h) 5* 8.0 =", 5* 8.0)
    print("i) 3.4 - 1.1 =", 3.4 - 1.1)
    print("j) 9 % 2 =", 9 % 2)
    print("k) 9 % -2 =", 9 % -2)
    print("l) -9 % 2 =", -9 % 2)

def sarcina_2():
    # Sarcina 2
    s = 2
    print("Rezultate pentru Sarcina 2:")
    print("a) bool(-30) =", bool(-30))
    print("b) bool(0.2) =", bool(0.2))
    print("c) bool(0) =", bool(0))
    print("d) bool(s) =", bool(s))
def sarcina_3():
    # Sarcina 3
    print("Rezultate pentru Sarcina 3:")
    print("a) type(5.8e+3) =", type(5.8e+3))
    print("b) type(1+4.0+2) =", type(1+4.0+2))
    print("c) type(None) =", type(None))
    print("d) type('float') =", type('float'))
    print("e) type(4+2j) =", type(4+2j))

def sarcina_4():
    # Sarcina 4
    x = 32
    y = 6

    #a
    z1 = x % y + y
    #b
    z2 = x // y + x
    #c
    y1 = x//y
    z3 = x // y1
    #d
    y2 = x // y + y
    z4 = x // y2 + y2
    #e
    y3 = x % y +2
    z5 = x % y3 + 2
    #f
    y4 = x//y
    z6 = x % (y4 + 2)
    #g
    y5 = x % y
    z8 = x // (y5 + 2)

    print(f"a) z= x%y+y = {z1}")
    print(f"b) z= x//y+x = {z2}")
    print(f"c) y= x//y={y1} z=x//y = {z3}")
    print(f"d) y= x//y+y ={y2} z= x//y = {z4}")
    print(f"e) y= x%y+2={y3} z= x%y+x ={z5}")
    print(f"f) y= x//y={y4} z= x%(y+2) ={z6}")
    print(f"g) y= x%y={y5} z= x//(y+2) ={z8}")

def sarcina_5():
    # Sarcina 5
    expr1 = 12.5 / 2.5
    expr2 = int(12.5) / int(2.5)
    expr3 = int(12.5 / 2.5)

    print(f"a) 12.5/2.5 = {expr1}")
    print(f"b) int(12.5)/int(2.5) = {expr2}")
    print(f"c) int(12.5/2.5) = {expr3}")

def sarcina_6():
    # Sarcina 6
    expr1 = str(6) * int('5')
    expr2 = int('6') + float('6.1')
    expr3 = str(6)*float('6.1')
    expr4 = str(6/4) * 2

    print(f"a) str(6)*int('5') = {expr1}")
    print(f"b) int('6')+float('6.1') = {expr2}")
    print(f"c) str(6)*float('6.1') = {expr3}")
    print(f"d) str(6/4)*2 = {expr4}")

def sarcina_7():
    # Sarcina 7
    x = 3
    y = 4

    print(f"a) {x}+{y}=?")
    print(f"b) ({x})({y})")
    print(f"c) x={x}; y={y};")
    print(f"d) Răspuns: ({x}; {y})")

def sarcina_8():
    # Sarcina 8
    x = int(input("Introduceți valoarea lui x: "))
    y = int(input("Introduceți valoarea lui y: "))

    z = (x**2 + y**2) * (x - y)**2
    print(f"Z = ({x}^2 + {y}^2) * ({x} - {y})^2 = {z}")

def sarcina_9():
    # Sarcina 9
    x = float(input("Introduceți valoarea lui x: "))

    if x < 0:
        f = x
    elif 0 <= x < 10:
        f = 2 * x
    elif 10 <= x < 100:
        f = 3 * x
    else:
        f = 4 * x

    print(f"f({x}) = {f}")

def sarcina_10():
    # Sarcina 10
    numar1 = int(input("Introduceți primul număr întreg: "))
    numar2 = int(input("Introduceți al doilea număr întreg: "))
    numar3 = int(input("Introduceți al treilea număr întreg: "))
    numar4 = int(input("Introduceți al patrulea număr întreg: "))

    sunt_distincte = True

    if numar1 == numar2 or numar1 == numar3 or numar1 == numar4:
        sunt_distincte = False
    elif numar2 == numar3 or numar2 == numar4:
        sunt_distincte = False
    elif numar3 == numar4:
        sunt_distincte = False

    # Afisăm rezultatul
    if sunt_distincte:
        print("Cele 4 numere sunt distincte.")
    else:
        print("Cele 4 numere NU sunt distincte.")

def sarcina_11():
    # Sarcina 11
 # Citim cele 3 numere întregi
    nr1 = int(input("Introduceți primul număr întreg: "))
    nr2 = int(input("Introduceți al doilea număr întreg: "))
    nr3 = int(input("Introduceți al treilea număr întreg: "))

# Verificăm dacă există un număr egal cu suma celorlalte două
    if nr1 == nr2 + nr3:
        print(f"{nr1} este egal cu suma {nr2} + {nr3}.")
    elif nr2 == nr1 + nr3:
        print(f"{nr2} este egal cu suma {nr1} + {nr3}.")
    elif nr3 == nr1 + nr2:
        print(f"{nr3} este egal cu suma {nr1} + {nr2}.")
    else:
        print("Nu există niciun număr care să fie egal cu suma celorlalte două.")

def individual():
    # Sarcina individuala
    import math

    d = 2.359
    f = 15.5

    results = []

    for x in range(-1, 30):
        x /= 10.0  # Convert x to a float with one decimal place
        q = abs(math.sqrt(d**2 + f) - f**2 * math.log(d + x)**3 / d)
        y = math.cos(q)**3 * q**4 - q / math.sqrt(f**2 + d**2)
        results.append((x, y))

    for x, y in results:
        print(f"y = {y:.6f}")

if __name__ == "__main__": #script este executat ca program principal
    main()


import csv
source = open("Protein.txt", "r")
matrix = [[], [], [], [], [], []]

for x in range(6):
	line = source.readline()
	while True:
		c = source.read(1)
		if c == '>' or c=='':
			break
		if (c!='\n' and c!='\r') :
			matrix[x].append(c)

compare = open("BLOSUM62.txt", "r")
compareator = [[0 for x in range(25)] for x in range(23)]
x=0
line = compare.readline()
for x in range(23):
	line = compare.readline()
	compareator[x]=line.split()
for x in range(23):
	compareator[x] = compareator[x][:-1]
	compareator[x] = compareator[x][1:]

distance = [[0 for x in range(6)] for x in range(6)]
keys={'A':0,'R':1,'N':2,'D':3,'C':4,'Q':5,'E':6,'G':7,'H':8,'I':9,'L':10,'K':11,'M':12,'F':13,'P':14,'S':15,'T':16,'W':17,'Y':18,'V':19,'B':20,'Z':21,'X':22 }
x=0
for x in range(6):
	for y in range(x+1,6):
		score=0
		p=len(matrix[x])
		q=len(matrix[y])
		for z in range(min(p,q)):
			a=matrix[x][z]
			b=matrix[y][z]
			score+=int(compareator[keys[a]][keys[b]])
		score=score-abs(p-q)*4
		distance[x][y]=score
		distance[y][x]=score

distance[0]=['Cow']+distance[0]
distance[1]=['Sheep']+distance[1]
distance[2]=['Worm']+distance[2]
distance[3]=['Rat']+distance[3]
distance[4]=['Frog']+distance[4]
distance[5]=['Pig']+distance[5]
Poutput = open("Pdistance.txt", "w")
print>>Poutput,"*,Cow,Sheep,Worm,Rat,Frog,Pig"
writer = csv.writer(Poutput)
for i in range(len(distance)):
	writer.writerow(distance[i])
Poutput.close()


# Phylogenetic Trees via Maximum Parsimony
# Ran Libeskind-Hadas
# April 2016

# This program uses the Maximum Parsimony Method and Nearest Neighbor
# Interchange to find a local optimum maximum parsimony phylogenetic tree
# with branch lengths.

# The input comprises a list of tip names, a list of characters, and
# a dictionary that associates each tip with a character.  The output is
# a newick tree with branch lengths, corrected using Jukes-Cantor
# correction.

import random
import math
import grasses_seq as gs
Infinity = float('inf')

# --------------------------------------------------------------------------
# Start test inputs

t1 = ["Linde", "Atwood", "West"]
m1 = {"Linde": "AAA" , "Atwood": "AAT", "West": "AAG"}

t2 = ["Groody", "Froody", "Snoody", "Wumph", "Glumph", "Humph"]
m2 = {"Groody": "AACA", "Froody": "AACG", "Snoody": "AAGC", "Wumph": "CCAA", "Glumph": "CTAA", "Humph": "AAAA"}

t3 = ["Alien", "Zombie", "Human"]
m3 = {"Alien": "GA", "Zombie": "GT", "Human": "CT"}

t4 = ["Western", "Eastern", "Northern", "Southern"]
m4 = {"Western": "TTAA", "Eastern": "AAAA", "Northern": "AACT", "Southern": "TACG"}

t5 = ["1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"]
m5 = {"1":"AAAT", "2":"AAAG", "3":"AAAC", "4":"ATAG", "5":"ATAC", "6":"ATAT", "7":"GGCC", "8":"GGTA", "9":"GGTC", "10":"GGCT", "11":"AAAA", "12":"AAAC"}

Groodyspecies = ["Groody", "Froody", "Snoody", "Wumph", "Glumph", "Humph"]
Groodymapping = {"Groody": "AACC", "Froody": "AACG", "Snoody": "AAGC", "Wumph": "CCAA", "Glumph": "CTAA", "Humph": "AAAA"}

# End test inputs
# --------------------------------------------------------------------------

# This is the best single position function.
# It takes a tree, a character (A, T, C, or G) for the root, a position,
# a tip mapping, and memo dictionary as input and returns the best cost
# of a maximum parsimony solution for the tree.
def bestSinglePosition(tree, character, position, tipMapping, memo):
    root = tree[0]
    leftTree = tree[1]
    rightTree = tree[2]
    if leftTree == ():  # Tip
        if character == tipMapping[root][position]:
            return 0
        else:
            return Infinity
    else:
        if (tree, character, position) in memo: return memo[(tree, character, position)]
        bestSolution = Infinity
        for leftChar in ["A", "T", "C", "G"]:
            for rightChar in ["A", "T", "C", "G"]:
                bestLeft = bestSinglePosition(leftTree, leftChar, position, tipMapping, memo)
                bestRight= bestSinglePosition(rightTree, rightChar, position, tipMapping, memo)
                cost = bestLeft + bestRight
                if leftChar != character: cost += 1
                if rightChar != character: cost += 1
                if cost < bestSolution:
                    bestSolution = cost
        memo[(tree, character, position)] = bestSolution
    return bestSolution

# This function calls best (above) four times, once for each possible
# symbol at the root node...
# For a given tree, position, and tipMapping, returns the
# solution of minimum cost for this tree and this position.
# The solution is of the form (cost, tree) where tree
# has all internal nodes changed to the character in the optimal
# solution.
def bestChar(tree, position, tipMapping):
    memo = {}
    bestSolution = Infinity
    for root in ["A", "T", "C", "G"]:
        option = bestSinglePosition(tree, root, position, tipMapping, memo)
        if option < bestSolution:
            bestSolution = option
    return bestSolution

# This function scores a tree by looping over each possible position in
# the tip sequence data, finding the best solution for each position,
# merging those separate trees into one tree, and then returns a tuple
# of the form (treeCost, bestTree) where treeCost is the total parsimony
# cost of that tree and bestTree is that tree with the full ancestral sequences
# at each internal node.
def maxParsimony(tree, tipMapping):
    keys = list(tipMapping.keys())
    positions = len(tipMapping[keys[0]])
    score = 0
    for position in range(positions):
        score += bestChar(tree, position, tipMapping)
    return score

# -----------------------------------------------------------------------
# NNI Heuristic

# Generate a single very lopsided RLR tree with the given set of tips.
# This can be used as the initial tree in our algorithm.
def oneTree(tipList):
    if len(tipList) == 1: return (tipList[0], (), ())
    else: return ("Anc", (tipList[0], (), ()), oneTree(tipList[1:]))

# Generate an initial tree by first shuffling the tipList and then calling
# the oneTree function above to build a tree with that shuffled tipList.
def randomTree(tipList):
    random.shuffle(tipList)
    return oneTree(tipList)

# The main NNI heuristic
def NNIheuristic(tipMapping, sampleSize):
    tipList = list(tipMapping.keys())
    bestTree = randomTree(tipList)
    bestScore = maxParsimony(bestTree,tipMapping)
    #print "Initial tree score ", bestScore
    while True:
        neighbors = allNNIs(bestTree)
        if sampleSize > len(neighbors): sampleSize = len(neighbors)
        sampleNeighbors = random.sample(neighbors, sampleSize)
        if len(sampleNeighbors) == 0: break
        scoredNeighbors = []
        for neighbor in sampleNeighbors:
            nScore  = maxParsimony(neighbor, tipMapping)
            scoredNeighbors.append((nScore, neighbor))
        scoredNeighbors.sort()
        if scoredNeighbors[0][0] >= bestScore: break
        bestScore = scoredNeighbors[0][0]
        #print " Best score so far ", bestScore
        bestTree = scoredNeighbors[0][1]
    #print "Best tree ", bestTree
    newickTree = RLRtoNewick(bestTree)
    #print "Final score: ", bestScore
    #print "Final newick tree: "
    return newickTree, bestScore

# Takes a root-left-right tree as input and returns all root-left-right
# trees obtained making the tree unrooted, re-rooting the tree at each
# internal branch, and computing the NNIs for that re-rooting.
def allNNIs(RLRtree):
    newickVersion = RLRtoNewick(RLRtree)
    allReroots = genAllTrees(newickVersion)
    newickNNIs = []
    RLRNNIs = []
    for tree in allReroots:
        newickNNIs.extend(NNIs(tree))
    for tree in newickNNIs:
        RLRNNIs = list(map(lambda tree: newickToRLR(tree), newickNNIs))
    return RLRNNIs

# Takes a newick tree (A, B) as input.  Implicitly, that tree can be
# viewed as rooted on the branch between A and B.  This function returns
# a list of all possible re-rootings of this same tree, where the root
# moves to the left of the original root.
def genLeft(newickTree):
    left = newickTree[0]
    right = newickTree[1]
    if type(left) != tuple: return []  # We can't push forward left!
    else:
        leftleft = left[0]
        leftright = left[1]
        tree1 = (leftleft, (leftright, right))
        tree2 = (leftright, (leftleft, right))
        return [tree1, tree2] + genLeft(tree1) + genLeft(tree2)

# Takes a newick tree (A, B) as input.  Implicitly, that tree can be
# viewed as rooted on the branch between A and B.  This function returns
# a list of all possible re-rootings of this same tree, where the root
# moves to the right of the original root.
def genRight(newickTree):
    left = newickTree[0]
    right = newickTree[1]
    if type(right) != tuple: return [] # We can't push forward right!
    else:
        rightleft = right[0]
        rightright = right[1]
        tree1 = ((left, rightleft), rightright)
        tree2 = ((left, rightright), rightleft)
        return [tree1, tree2] + genRight(tree1) + genRight(tree2)

# Takes a newick tree (A, B) as input and returns a list of all possible
# re-rootings of this tree.
def genAllTrees(newickTree):
    return genLeft(newickTree) + genRight(newickTree)

# Take a newick tree ((A, B). (C, D)) as input and returns a list of the two
# NNI neighbors of that tree.
def NNIs(newickTree):
    candidates = filter(lambda tree: type(tree[0]) == tuple and \
                            type(tree[1]) == tuple, genAllTrees(newickTree))
    NNIlist = []
    for tree in candidates:
        A = tree[0][0]
        B = tree[0][1]
        C = tree[1][0]
        D = tree[1][1]
        NNIlist.append( ((A, C), (B, D)) )
        NNIlist.append( ((A, D), (C, B)) )
    return NNIlist

# Convert a root-left-right tree to plain newick format.
def RLRtoNewick(RLRTree):
    root = RLRTree[0]
    left = RLRTree[1]
    right = RLRTree[2]
    if len(left) == 0: return root
    else: return (RLRtoNewick(left), RLRtoNewick(right))

# Convert a plain newick tree to root-left-right format where the internal
# nodes are all named "Anc" for "Ancestor".
def newickToRLR(newickTree):
    if type(newickTree) != tuple: return (newickTree, (), ())
    else:
        return ("Anc", newickToRLR(newickTree[0]), newickToRLR(newickTree[1]))


from PySide6.QtWidgets import (
    QComboBox,
    QGroupBox,
    QHBoxLayout,
    QLabel,
    QVBoxLayout,
)

from game.ato.flight import Flight
from game.ato.starttype import StartType
from game.theater import OffMapSpawn
from qt_ui.models import PackageModel

class QFlightStartType(QGroupBox):
    def __init__(self, package_model: PackageModel, flight: Flight):
        super().__init__()
        self.package_model = package_model
        self.flight = flight

        self.layout = QVBoxLayout()
        self.main_row = QHBoxLayout()
        self.start_type_label = QLabel("Start type:")
        self.start_type = QComboBox()

        for start_type in StartType:
            self.start_type.addItem(start_type.value, start_type)
        self.start_type.setCurrentText(flight.start_type.value)

        if isinstance(self.flight.departure, OffMapSpawn):
            self.start_type.setEnabled(False)

        self.start_type.currentTextChanged.connect(self._on_start_type_selected)
        self.main_row.addWidget(self.start_type_label)
        self.main_row.addWidget(self.start_type)

        self.layout.addLayout(self.main_row)
        self.layout.addWidget(
            QLabel(
                "Any option other than Cold will make this flight non-targetable "
                + "by OCA/Aircraft missions. This will affect game balance."
            )
        )
        self.setLayout(self.layout)

    def _on_start_type_selected(self):
        selected = self.start_type.currentData()
        self.flight.start_type = selected
        self.package_model.update_tot()


''' Scheduling Aircraft Landing (Static Case) with multiple runways
    Landing times of aircrafts are determined satisfying certain constraints
    Constraints- The aircraft should land within a predetermined time interval
                 Clearance time between two landings should be satisfied if they are landing on same runway
'''
import numpy as np
from gurobipy import *
import os

#=====================================================================
# Function to fetch data from provided txt file
# @parameters- file_name : Data file(must be present in same directory)
#=====================================================================
def fetch_data(file_name):
    data=open(os.getcwd()+'\\data_files\\'+file_name,'r')
    lines=data.readlines()
    num_planes=int(lines[0].split()[0])
    freeze_time=int(lines[0].split()[1])

    flight_details=np.empty([num_planes,6],dtype=float)
    sep_time=np.empty([num_planes,num_planes],dtype=int)
    s=''
    for line in lines[1:]:
        s=s+line
    s=s.split()
    flag=0
    count=0
    for items in [s[x:x+6+num_planes] for x in range(0,len(s),num_planes+6)]:
        flight_details[count]=[float(x) for x in items[:6]]
        sep_time[count]=[int(x) for x in items[6:]]
        count=count+1
    print(flight_details)
    print(sep_time)
    data.close()
    return num_planes,flight_details,sep_time

#=====================================================================
# Function to find landing time of the aircrafts
# @parameters- file_name Data file(must be present in same directory)
#              Number of runways
#=====================================================================
def schedule(file_,runways):
    num_flights,flights,clearance=fetch_data(file_)
    try:
        #Creating a Gurobi model
        model=Model("Aircraft Landing Schedule")
        M=max(flights[:,3])-min(flights[:,1])
        z_pos={}
        for i in np.arange(1,num_flights+1):
            z_pos[i]=flights[i-1,5]
        z_neg={}
        for i in np.arange(1,num_flights+1):
            z_neg[i]=flights[i-1,4]
        x_={}
        for i in np.arange(1,num_flights+1):
            x_[i]=0
        del_={}
        q_={}
        for i in np.arange(1,num_flights+1):
            for j in np.arange(1,num_flights+1):
                del_[i,j]=0
                q_[i,j]=0
        y_={}
        for i in np.arange(1,num_flights+1):
            for r in np.arange(1,runways+1):
                y_[i,r]=0

        #Adding decision variables
        z_p=model.addVars(z_pos.keys(),lb=0,ub=GRB.INFINITY,obj=z_pos,vtype=GRB.CONTINUOUS,name="z_p")
        z_n=model.addVars(z_neg.keys(),lb=0,ub=GRB.INFINITY,obj=z_neg,vtype=GRB.CONTINUOUS,name="z_n")
        x=model.addVars(x_.keys(),lb=0,ub=GRB.INFINITY,obj=x_,vtype=GRB.CONTINUOUS,name="x")
        d=model.addVars(del_.keys(),lb=0,ub=1,obj=del_,vtype=GRB.BINARY,name="d")
        y=model.addVars(y_.keys(),lb=0,ub=1,obj=y_,vtype=GRB.BINARY,name="y")
        q=model.addVars(q_.keys(),lb=0,ub=1,obj=q_,vtype=GRB.BINARY,name="q")

        #Adding constraints
        model.addConstrs((x[j]-x[i]>=clearance[i-1,j-1]*q[j,i] - (d[j,i])*M for i in np.arange(1,num_flights+1) for j in np.arange(1,num_flights+1) if j!=i),name="Clearance")
        model.addConstrs((z_p[i]>=x[i]-flights[i-1,2] for i in np.arange(1,num_flights+1)),name="+")
        model.addConstrs((z_n[i]>=flights[i-1,2]-x[i] for i in np.arange(1,num_flights+1)),name="-")
        model.addConstrs((x[i]>=flights[i-1,1] for i in np.arange(1,num_flights+1)),name="Land after earliest landing time")
        model.addConstrs((x[i]<=flights[i-1,3] for i in np.arange(1,num_flights+1)),name="Land before latest landing time")
        model.addConstrs((d[i,j]+d[j,i]==1 for i in np.arange(1,num_flights +1) for j in np.arange(1,num_flights+1) if j!=i),name="~")
        model.addConstrs((q[i,j]==q[j,i] for i in np.arange(1,num_flights +1) for j in np.arange(1,num_flights+1) if j!=i),name="$")
        model.addConstrs((quicksum(y[i,r] for r in np.arange(1,runways+1))==1 for i in np.arange(1,num_flights+1)),name="Land at only 1 runway")
        model.addConstrs((q[i,j]>=y[i,r]+y[j,r]-1 for r in np.arange(1,runways+1) for j in np.arange(1,num_flights+1) for i in np.arange(1,num_flights+1) if j!=i),name="enforcing constraint")

        model.optimize()

        # Displaying scheduled landing times

        for i in np.arange(1,num_flights+1):
            for r in np.arange(1,runways+1):
                if model.getVarByName("y["+str(i)+","+str(r)+"]").X==1:
                    print('%s %g %s %g' % ('SCHEDULED LANDING TIME FOR AIRCRAFT '+str(i)+" = ", model.getVarByName("x["+str(i)+"]").X, ' AT RUNWAY= ',r))
                    #print('at runway= '+str(r))
        '''
        for v in model.getVars():
            if (v.x>=0):
                print('%s %g' % (v.varName, v.x))'''

    except GurobiError as e:
        print('Error code ' + str(e.errno) + ": " + str(e))

    except AttributeError as a:
        print('Encountered an attribute error '+str(a))

    return model

a='''The available cases are:
    [1] airland1.txt with 10 flights
    [2] airland8.txt with 50 flights
    [3] airland13.txt with 500 flights
    '''

print(a)
num=input('Enter your choice (1/2/3):')
runway=input('Enter number of runways available')
if num==1:
    schedule('airland1.txt',runway)
elif num==2:
    schedule('airland8.txt',runway)
elif num==3:
    schedule('airland13.txt',runway)

'''Print extracted data'''
#fetch_data('airland13.txt')


import socket

class ACPCCommunication:
    def __init__(self, sock=None):
        if sock is None:
            self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        else:
            self.sock = sock

        self.name = "ACPCCommunication"
        self.MSGLEN = 1024

    def connect(self, host, port):
        self.sock.connect((host, port))

    def handshake(self):
        self.send_line('VERSION:2.0.0')

    def send_line(self, line):
        totalsent = 0
        msg = line + '\r\n'
        while totalsent < len(msg):
            sent = self.sock.send(msg[totalsent:].encode())
            if sent == 0:
                raise RuntimeError("socket connection broken while sending")
            totalsent = totalsent + sent
        return totalsent

    def get_line(self):
        chunks = []
        bytes_recd = 0
        while bytes_recd < self.MSGLEN:
            chunk = self.sock.recv(min(self.MSGLEN - bytes_recd, 2048))
            if chunk == b'':
                raise RuntimeError("socket connection broken while reading")
            chunks.append(chunk)
            bytes_recd = bytes_recd + len(chunk)
            if chunk[-2:] == b'\r\n':
                break
        return b''.join(chunks).decode()

    def close(self):
        self.sock.close()