from __future__ import print_function, division

import argparse
import sys
import re
import xlsxwriter

_comparer = None

def FeatureScoreComparer(sortingMetric):
    global _comparer
    _comparer = {
        'gain': lambda x: -x.Gain,
        'fscore': lambda x: -x.FScore,
        'fscoreweighted': lambda x: -x.FScoreWeighted,
        'fscoreweightedaverage': lambda x: -x.FScoreWeightedAverage,
        'averagegain': lambda x: -x.AverageGain,
        'expectedgain': lambda x: -x.ExpectedGain
    }[sortingMetric.lower()]

class SplitValueHistogram:
    def __init__(self):
        self.values = {}

    def AddValue(self, splitValue, count):
        if not (splitValue in self.values):
            self.values[splitValue] = 0
        self.values[splitValue] += count

    def Merge(self, histogram):
        for key in histogram.values.keys():
            self.AddValue(key, histogram.values[key])

class FeatureInteraction:
    def __init__(self, interaction, gain, cover, pathProbability, depth, treeIndex, fScore=1):
        self.SplitValueHistogram = SplitValueHistogram()

        features = sorted(interaction, key=lambda x: x.Feature)
        self.Name = "|".join(x.Feature for x in features)

        self.Depth = len(interaction) - 1
        self.Gain = gain
        self.Cover = cover
        self.FScore = fScore
        self.FScoreWeighted = pathProbability

        self.AverageFScoreWeighted = self.FScoreWeighted / self.FScore
        self.AverageGain = self.Gain / self.FScore
        self.ExpectedGain = self.Gain * pathProbability
        self.TreeIndex = treeIndex
        self.TreeDepth = depth
        self.AverageTreeIndex = self.TreeIndex / self.FScore
        self.AverageTreeDepth = self.TreeDepth / self.FScore
        self.HasLeafStatistics = False

        if self.Depth == 0:
            self.SplitValueHistogram.AddValue(interaction[0].SplitValue, 1)

        self.SumLeafValuesLeft = 0.0
        self.SumLeafCoversLeft = 0.0
        self.SumLeafValuesRight = 0.0
        self.SumLeafCoversRight = 0.0

    def __lt__(self, other):
        return self.Name < other.Name

class FeatureInteractions:
    def __init__(self):
        self.Count = 0
        self.interactions = {}

    def GetFeatureInteractionsOfDepth(self, depth):
        return sorted([self.interactions[key] for key in self.interactions.keys() if self.interactions[key].Depth == depth], key=_comparer)

    def GetFeatureInteractionsWithLeafStatistics(self):
        return sorted([self.interactions[key] for key in self.interactions.keys() if self.interactions[key].HasLeafStatistics], key=_comparer)

    def Merge(self, other):
        for key in other.interactions.keys():
            fi = other.interactions[key]
            if not (key in self.interactions):
                self.interactions[key] = fi
            else:
                self.interactions[key].Gain += fi.Gain
                self.interactions[key].Cover += fi.Cover
                self.interactions[key].FScore += fi.FScore
                self.interactions[key].FScoreWeighted += fi.FScoreWeighted
                self.interactions[key].AverageFScoreWeighted = self.interactions[key].FScoreWeighted / self.interactions[key].FScore
                self.interactions[key].AverageGain = self.interactions[key].Gain / self.interactions[key].FScore
                self.interactions[key].ExpectedGain += fi.ExpectedGain
                self.interactions[key].SumLeafCoversLeft += fi.SumLeafCoversLeft
                self.interactions[key].SumLeafCoversRight += fi.SumLeafCoversRight
                self.interactions[key].SumLeafValuesLeft += fi.SumLeafValuesLeft
                self.interactions[key].SumLeafValuesRight += fi.SumLeafValuesRight
                self.interactions[key].TreeIndex += fi.TreeIndex
                self.interactions[key].AverageTreeIndex = self.interactions[key].TreeIndex / self.interactions[key].FScore
                self.interactions[key].TreeDepth += fi.TreeDepth
                self.interactions[key].AverageTreeDepth = self.interactions[key].TreeDepth / self.interactions[key].FScore

                self.interactions[key].SplitValueHistogram.Merge(fi.SplitValueHistogram)

class XgbModel:
    def __init__(self, verbosity=0):
        self._verbosity = verbosity
        self.XgbTrees = []
        self._treeIndex = 0
        self._maxDeepening = 0
        self._pathMemo = []
        self._maxInteractionDepth = 0

    def AddTree(self, tree):
        self.XgbTrees.append(tree)

    def GetFeatureInteractions(self, maxInteractionDepth, maxDeepening):
        xgbFeatureInteractions = FeatureInteractions()
        self._maxInteractionDepth = maxInteractionDepth
        self._maxDeepening = maxDeepening

        if self._verbosity >= 1:
            if self._maxInteractionDepth == -1:
                print("Collectiong feature interactions")
            else:
                print("Collectiong feature interactions up to depth {}".format(self._maxInteractionDepth))

        for i, tree in enumerate(self.XgbTrees):
            if self._verbosity >= 2:
                sys.stdout.write("Collectiong feature interactions within tree #{} ".format(i + 1))

            self._treeFeatureInteractions = FeatureInteractions()
            self._pathMemo = []
            self._treeIndex = i

            treeNodes = []
            self.CollectFeatureInteractions(tree, treeNodes, currentGain=0.0, currentCover=0.0, pathProbability=1.0, depth=0, deepening=0)

            if self._verbosity >= 2:
                sys.stdout.write("=> number of interactions: {}\n".format(len(self._treeFeatureInteractions.interactions)))
            xgbFeatureInteractions.Merge(self._treeFeatureInteractions)

        if self._verbosity >= 1:
            print("{} feature interactions has been collected.".format(len(xgbFeatureInteractions.interactions)))

        return xgbFeatureInteractions

    def CollectFeatureInteractions(self, tree, currentInteraction, currentGain, currentCover, pathProbability, depth, deepening):
        if tree.node.IsLeaf:
            return

        currentInteraction.append(tree.node)
        currentGain += tree.node.Gain
        currentCover += tree.node.Cover

        pathProbabilityLeft = pathProbability * (tree.left.node.Cover / tree.node.Cover)
        pathProbabilityRight = pathProbability * (tree.right.node.Cover / tree.node.Cover)

        fi = FeatureInteraction(currentInteraction, currentGain, currentCover, pathProbability, depth, self._treeIndex, 1)

        if (depth < self._maxDeepening) or (self._maxDeepening < 0):
            newInteractionLeft = []
            newInteractionRight = []

            self.CollectFeatureInteractions(tree.left, newInteractionLeft, 0.0, 0.0, pathProbabilityLeft, depth + 1, deepening + 1)
            self.CollectFeatureInteractions(tree.right, newInteractionRight, 0.0, 0.0, pathProbabilityRight, depth + 1, deepening + 1)

        path = ",".join(str(n.Number) for n in currentInteraction)

        if not (fi.Name in self._treeFeatureInteractions.interactions):
            self._treeFeatureInteractions.interactions[fi.Name] = fi
            self._pathMemo.append(path)
        else:
            if path in self._pathMemo:
                return
            self._pathMemo.append(path)

            tfi = self._treeFeatureInteractions.interactions[fi.Name]
            tfi.Gain += currentGain
            tfi.Cover += currentCover
            tfi.FScore += 1
            tfi.FScoreWeighted += pathProbability
            tfi.AverageFScoreWeighted = tfi.FScoreWeighted / tfi.FScore
            tfi.AverageGain = tfi.Gain / tfi.FScore
            tfi.ExpectedGain += currentGain * pathProbability
            tfi.TreeDepth += depth
            tfi.AverageTreeDepth = tfi.TreeDepth / tfi.FScore
            tfi.TreeIndex += self._treeIndex
            tfi.AverageTreeIndex = tfi.TreeIndex / tfi.FScore
            tfi.SplitValueHistogram.Merge(fi.SplitValueHistogram)

        if (len(currentInteraction) - 1 == self._maxInteractionDepth):
            return

        currentInteractionLeft = list(currentInteraction)
        currentInteractionRight = list(currentInteraction)

        leftTree = tree.left
        rightTree = tree.right

        if (leftTree.node.IsLeaf and (deepening == 0)):
            tfi = self._treeFeatureInteractions.interactions[fi.Name]
            tfi.SumLeafValuesLeft += leftTree.node.LeafValue
            tfi.SumLeafCoversLeft += leftTree.node.Cover
            tfi.HasLeafStatistics = True

        if (rightTree.node.IsLeaf and (deepening == 0)):
            tfi = self._treeFeatureInteractions.interactions[fi.Name]
            tfi.SumLeafValuesRight += rightTree.node.LeafValue
            tfi.SumLeafCoversRight += rightTree.node.Cover
            tfi.HasLeafStatistics = True

        self.CollectFeatureInteractions(tree.left, currentInteractionLeft, currentGain, currentCover, pathProbabilityLeft, depth + 1, deepening)
        self.CollectFeatureInteractions(tree.right, currentInteractionRight, currentGain, currentCover, pathProbabilityRight, depth + 1, deepening)

class XgbTreeNode:
    def __init__(self):
        self.Feature = ''
        self.Gain = 0.0
        self.Cover = 0.0
        self.Number = -1
        self.LeftChild = None
        self.RightChild = None
        self.LeafValue = 0.0
        self.SplitValue = 0.0
        self.IsLeaf = False

    def __lt__(self, other):
        return self.Number < other.Number

class XgbTree:
    def __init__(self, node):
        self.left = None
        self.right = None
        self.node = node  # or node.copy()

class XgbModelParser:
    def __init__(self, verbosity=0):
        self._verbosity = verbosity
        self.nodeRegex = re.compile(r'(\d+):\[(.*?)(?:<(.+)|)\]\syes=(.*),no=(.*?),(?:missing=.*,)?gain=(.*),cover=(.*)')
        self.leafRegex = re.compile(r'(\d+):leaf=(.*),cover=(.*)')

    def ConstructXgbTree(self, tree):
        if tree.node.LeftChild is not None:
            tree.left = XgbTree(self.xgbNodeList[tree.node.LeftChild])
            self.ConstructXgbTree(tree.left)
        if tree.node.RightChild is not None:
            tree.right = XgbTree(self.xgbNodeList[tree.node.RightChild])
            self.ConstructXgbTree(tree.right)

    def ParseXgbTreeNode(self, line):
        node = XgbTreeNode()

        m = self.leafRegex.match(line)
        if m:
            node.Number = int(m.group(1))
            node.LeafValue = float(m.group(2))
            node.Cover = float(m.group(3))
            node.IsLeaf = True
        else:
            m = self.nodeRegex.match(line)
            node.Number = int(m.group(1))
            node.Feature = m.group(2)
            # split on boolean if m.group(3) is not captured
            node.SplitValue = float(m.group(3)) if m.group(3) else 0.5
            node.LeftChild = int(m.group(4))
            node.RightChild = int(m.group(5))
            node.Gain = float(m.group(6))
            node.Cover = float(m.group(7))
            node.IsLeaf = False
        return node

    def GetXgbModelFromFile(self, fileName, maxTrees):
        model = XgbModel(self._verbosity)
        self.xgbNodeList = {}
        numTree = 0
        with open(fileName) as f:
            for line in f:
                line = line.strip()
                if (not line) or line.startswith('booster'):
                    if any(self.xgbNodeList):
                        numTree += 1
                        if self._verbosity >= 2:
                            sys.stdout.write("Constructing tree #{}\n".format(numTree))
                        tree = XgbTree(self.xgbNodeList[0])
                        self.ConstructXgbTree(tree)

                        model.AddTree(tree)
                        self.xgbNodeList = {}
                        if numTree == maxTrees:
                            if self._verbosity >= 1:
                                print('maxTrees reached')
                            break
                else:
                    node = self.ParseXgbTreeNode(line)
                    if not node:
                        return None
                    self.xgbNodeList[node.Number] = node

            if any(self.xgbNodeList) and ((maxTrees < 0) or (numTree < maxTrees)):
                numTree += 1
                if self._verbosity >= 2:
                    sys.stdout.write("Constructing tree #{}\n".format(numTree))
                tree = XgbTree(self.xgbNodeList[0])
                self.ConstructXgbTree(tree)

                model.AddTree(tree)
                self.xgbNodeList = {}

        return model

    def GetXgbModelFromMemory(self, dump, maxTrees):
        model = XgbModel(self._verbosity)
        self.xgbNodeList = {}
        numTree = 0
        for booster_line in dump:
            self.xgbNodeList = {}
            for line in booster_line.split('\n'):
                line = line.strip()
                if not line:
                    continue
                node = self.ParseXgbTreeNode(line)
                if not node:
                    return None
                self.xgbNodeList[node.Number] = node
            numTree += 1
            tree = XgbTree(self.xgbNodeList[0])
            self.ConstructXgbTree(tree)
            model.AddTree(tree)
            if numTree == maxTrees:
                break
        return model

def rankInplace(a):
    c = [(j, i[0]) for j, i in enumerate(sorted(enumerate(a), key=lambda x:x[1]))]
    c.sort(key=lambda x: x[1])
    return [i[0] for i in c]

def FeatureInteractionsWriter(FeatureInteractions, fileName, MaxDepth, topK, MaxHistograms, verbosity=0):

    if verbosity >= 1:
        print("Writing {}".format(fileName))

    workbook = xlsxwriter.Workbook(fileName)

    cf_first_row = workbook.add_format()
    cf_first_row.set_align('center')
    cf_first_row.set_align('vcenter')
    cf_first_row.set_bold(True)

    cf_first_column = workbook.add_format()
    cf_first_column.set_align('center')
    cf_first_column.set_align('vcenter')

    cf_num = workbook.add_format()
    cf_num.set_num_format('0.00')

    for depth in range(MaxDepth + 1):
        if verbosity >= 1:
            print("Writing feature interactions with depth {}".format(depth))

        interactions = FeatureInteractions.GetFeatureInteractionsOfDepth(depth)

#        KTotalGain = sum([i.Gain for i in interactions])
#        TotalCover = sum([i.Cover for i in interactions])
#        TotalFScore = sum([i.FScore for i in interactions])
#        TotalFScoreWeighted = sum([i.FScoreWeighted for i in interactions])
#        TotalFScoreWeightedAverage = sum([i.AverageFScoreWeighted for i in interactions])

        if topK > 0:
            interactions = interactions[0:topK]

        if not interactions:
            break

        ws = workbook.add_worksheet("Interaction Depth {}".format(depth))

        ws.set_row(0, 20, cf_first_row)

        ws.set_column(0, 0, max([len(i.Name) for i in interactions]) + 10, cf_first_column)

        ws.set_column(1, 13, 17)
        ws.set_column(10, 11, 18)
        ws.set_column(12, 12, 19)
        ws.set_column(13, 13, 17, cf_num)
        ws.set_column(14, 15, 19, cf_num)

        for col, name in enumerate(["Interaction", "Gain", "FScore", "wFScore", "Average wFScore",
                                    "Average Gain", "Expected Gain", "Gain Rank", "FScore Rank",
                                    "wFScore Rank", "Avg wFScore Rank", "Avg Gain Rank",
                                    "Expected Gain Rank", "Average Rank", "Average Tree Index",
                                    "Average Tree Depth"]):
            ws.write(0, col, name)

        gainSorted = rankInplace([-f.Gain for f in interactions])
        fScoreSorted = rankInplace([-f.FScore for f in interactions])
        fScoreWeightedSorted = rankInplace([-f.FScoreWeighted for f in interactions])
        averagefScoreWeightedSorted = rankInplace([-f.AverageFScoreWeighted for f in interactions])
        averageGainSorted = rankInplace([-f.AverageGain for f in interactions])
        expectedGainSorted = rankInplace([-f.ExpectedGain for f in interactions])

        for i, fi in enumerate(interactions):
            ws.write(i + 1, 0, fi.Name)
            ws.write(i + 1, 1, fi.Gain)
            ws.write(i + 1, 2, fi.FScore)
            ws.write(i + 1, 3, fi.FScoreWeighted)
            ws.write(i + 1, 4, fi.AverageFScoreWeighted)
            ws.write(i + 1, 5, fi.AverageGain)
            ws.write(i + 1, 6, fi.ExpectedGain)
            ws.write(i + 1, 7, 1 + gainSorted[i])
            ws.write(i + 1, 8, 1 + fScoreSorted[i])
            ws.write(i + 1, 9, 1 + fScoreWeightedSorted[i])
            ws.write(i + 1, 10, 1 + averagefScoreWeightedSorted[i])
            ws.write(i + 1, 11, 1 + averageGainSorted[i])
            ws.write(i + 1, 12, 1 + expectedGainSorted[i])
            ws.write(i + 1, 13, (6.0 + gainSorted[i] + fScoreSorted[i] + fScoreWeightedSorted[i] + averagefScoreWeightedSorted[i] + averageGainSorted[i] + expectedGainSorted[i]) / 6.0)
            ws.write(i + 1, 14, fi.AverageTreeIndex)
            ws.write(i + 1, 15, fi.AverageTreeDepth)

    interactions = FeatureInteractions.GetFeatureInteractionsWithLeafStatistics()
    if interactions:
        if verbosity >= 1:
            print("Writing leaf statistics")

        ws = workbook.add_worksheet("Leaf Statistics")

        ws.set_row(0, 20, cf_first_row)
        ws.set_column(0, 0, max([len(i.Name) for i in interactions]) + 10, cf_first_column)
        ws.set_column(1, 4, 20)

        for col, name in enumerate(["Interaction", "Sum Leaf Values Left", "Sum Leaf Values Right",
                                   "Sum Leaf Covers Left", "Sum Leaf Covers Right"]):
            ws.write(0, col, name)

        for i, fi in enumerate(interactions):
            ws.write(i + 1, 0, fi.Name)
            ws.write(i + 1, 1, fi.SumLeafValuesLeft)
            ws.write(i + 1, 2, fi.SumLeafValuesRight)
            ws.write(i + 1, 3, fi.SumLeafCoversLeft)
            ws.write(i + 1, 4, fi.SumLeafCoversRight)

    interactions = FeatureInteractions.GetFeatureInteractionsOfDepth(0)
    if interactions:
        if verbosity >= 1:
            print("Writing split value histograms")

        ws = workbook.add_worksheet("Split Value Histograms")

        ws.set_row(0, 20, cf_first_row)
        ws.set_column(0, 0, max([len(i.Name) for i in interactions]) + 10, cf_first_column)
        ws.set_column(1, 4, 20)

        for col, name in enumerate(["Interaction", "Sum Leaf Values Left", "Sum Leaf Values Right",
                                   "Sum Leaf Covers Left", "Sum Leaf Covers Right"]):
            ws.write(0, col, name)

        for i, fi in enumerate(interactions):
            if i >= MaxHistograms:
                break

            c1 = i * 2
            c2 = c1 + 1

            ws.merge_range(0, c1, 0, c2, fi.Name)
            ws.set_column(c1, c1, max(10, (len(fi.Name) + 4) / 2))
            ws.set_column(c2, c2, max(10, (len(fi.Name) + 4) / 2))

            for j, key in enumerate(sorted(fi.SplitValueHistogram.values.keys())):
                ws.write(j + 1, c1, key)
                ws.write(j + 1, c2, fi.SplitValueHistogram.values[key])

    workbook.close()

def main(argv):
    epilog = '''
XGBoost Feature Interactions Reshaped v0.3.1
URL: https://github.com/limexp/xgbfir
'''

    arg_parser = argparse.ArgumentParser(
        prog=argv[0],
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description='XGBoost model dump parser, which ranks features as well as feature interactions by different metrics.',
        epilog=epilog)
    arg_parser.add_argument(
        '-V', '--version',
        action='version',
        version='XGBoost Feature Interactions Reshaped 0.2')

    arg_parser.add_argument('-m',
                            dest='XgbModelFile', action='store',
                            default='xgb.dump',
                            help='Xgboost model dump (dumped w/ \'with_stats=True\')')
    arg_parser.add_argument('-o',
                            dest='OutputXlsxFile', action='store',
                            default='XgbFeatureInteractions.xlsx',
                            help='Xlsx file to be written')
    arg_parser.add_argument('-t',
                            dest='MaxTrees', action='store',
                            default='100', type=int,
                            help='Upper bound for trees to be parsed')
    arg_parser.add_argument('-d',
                            dest='MaxInteractionDepth', action='store',
                            default='2', type=int,
                            help='Upper bound for extracted feature interactions depth')
    arg_parser.add_argument('-g',
                            dest='MaxDeepening', action='store',
                            default='-1', type=int,
                            help='Upper bound for interaction start deepening \
                                  (zero deepening => interactions starting @root only)')
    arg_parser.add_argument('-k',
                            dest='TopK', action='store',
                            default='100', type=int,
                            help='Upper bound for exported feature interactions per depth level')
    arg_parser.add_argument('-H',
                            dest='MaxHistograms', action='store',
                            default='10', type=int,
                            help='Maximum number of histograms')
    arg_parser.add_argument('-s',
                            dest='SortBy', action='store',
                            default='Gain',
                            help='Score metric to sort by (Gain, FScore, wFScore, \
                                  AvgwFScore, AvgGain, ExpGain)')
    arg_parser.add_argument('-v', '--verbosity',
                            dest='Verbosity', action='count',
                            default='2',
                            help='Increate output verbosity')

    args = arg_parser.parse_args(args=argv[1:])

    args.XgbModelFile = args.XgbModelFile.strip()
    args.OutputXlsxFile = args.OutputXlsxFile.strip()

    verbosity = int(args.Verbosity)

    settings_print = '''
Settings:
=========
XgbModelFile (-m): {model}
OutputXlsxFile (-o): {output}
MaxInteractionDepth: {depth}
MaxDeepening (-g): {deepening}
MaxTrees (-t): {trees}
TopK (-k): {topk}
SortBy (-s): {sortby}
MaxHistograms (-H): {histograms}
'''.format(model=args.XgbModelFile,
           output=args.OutputXlsxFile,
           depth=args.MaxInteractionDepth,
           deepening=args.MaxDeepening,
           trees=args.MaxTrees,
           topk=args.TopK,
           sortby=args.SortBy,
           histograms=args.MaxHistograms)

    if verbosity >= 1:
        print(settings_print)

    FeatureScoreComparer(args.SortBy)

    xgbParser = XgbModelParser(verbosity)
    xgbModel = xgbParser.GetXgbModelFromFile(args.XgbModelFile, args.MaxTrees)
    featureInteractions = xgbModel.GetFeatureInteractions(args.MaxInteractionDepth, args.MaxDeepening)

    FeatureInteractionsWriter(featureInteractions, args.OutputXlsxFile, args.MaxInteractionDepth, args.TopK, args.MaxHistograms)

    if verbosity >= 1:
        print(epilog)

    return 0

def entry_point():
    """Zero-argument entry point for use with setuptools/distribute."""
    raise SystemExit(main(sys.argv))

def saveXgbFI(booster, feature_names=None, OutputXlsxFile='XgbFeatureInteractions.xlsx', MaxTrees=100, MaxInteractionDepth=2, MaxDeepening=-1, TopK=100, MaxHistograms=10, SortBy='Gain'):
    if 'get_dump' not in dir(booster):
        if 'get_booster' in dir(booster):
            booster = booster.get_booster()
        elif 'booster' in dir(booster):
            booster = booster.booster()
        else:
            return -20
    if feature_names is not None:
        if isinstance(feature_names, list):
            booster.feature_names = feature_names
        else:
            booster.feature_names = list(feature_names)
    FeatureScoreComparer(SortBy)
    xgbParser = XgbModelParser()
    dump = booster.get_dump('', with_stats=True)
    xgbModel = xgbParser.GetXgbModelFromMemory(dump, MaxTrees)
    featureInteractions = xgbModel.GetFeatureInteractions(MaxInteractionDepth, MaxDeepening)
    FeatureInteractionsWriter(featureInteractions, OutputXlsxFile, MaxInteractionDepth, TopK, MaxHistograms)

if __name__ == '__main__':
    entry_point()


class Main:
    def __init__(self, d):
        self.run = False
        self.vid = cv2.VideoCapture(0)      # capture video with webcam
        self.hand = HandTracking(nb_hands=1)
        self.c = d
        self.DEFAULT = ((123, 0, 123), 2)
        self.DEFAULT_VALUE = -1
        self.font = cv2.FONT_HERSHEY_SCRIPT_SIMPLEX
        self.init_value()
        self.speed = 5

    def init_value(self):
        self.value1 = self.DEFAULT_VALUE
        self.value2 = self.DEFAULT_VALUE
        self.value3 = self.DEFAULT_VALUE
        self.value4 = self.DEFAULT_VALUE
        self.value5 = self.DEFAULT_VALUE
        self.value6 = self.DEFAULT_VALUE
        self.value7 = self.DEFAULT_VALUE
        self.value8 = self.DEFAULT_VALUE
        self.value9 = self.DEFAULT_VALUE
        self.value10 = self.DEFAULT_VALUE
        self.value11 = self.DEFAULT_VALUE
        self.value12 = self.DEFAULT_VALUE

    def winsize(self, width=1000, height=800):
        """change the size of the windows cam"""
        self.vid.set(3, width), self.vid.set(4, height)

    def running(self):
        """change the value of run variable"""
        self.run = True

    @staticmethod
    def show(img, title="my capture video"):
        """this function aloaws to screening image"""
        cv2.imshow(title, img)
        cv2.waitKey(1)

    def draw_number(self, img, font):
        """ create same text in the rectagle """
        color = (255, 5, 10)
        cv2.putText(img, str(self.c["1v"]), (64, 220), font, 5, color, 5)
        cv2.putText(img, str(self.c["4v"]), (64, 365), font, 5, color, 5)
        cv2.putText(img, str(self.c["7v"]), (64, 520), font, 5, color, 5)
        cv2.putText(img, str(self.c["+v"]), (64, 680), font, 5, color, 5)
        cv2.putText(img, str(self.c["2v"]), (256, 220), font, 5, color, 5)
        cv2.putText(img, str(self.c["5v"]), (256, 370), font, 5, color, 5)
        cv2.putText(img, str(self.c["8v"]), (256, 520), font, 5, color, 5)
        cv2.putText(img, str(self.c["0v"]), (256, 680), font, 5, color, 5)
        cv2.putText(img, str(self.c["3v"]), (450, 220), font, 5, color, 5)
        cv2.putText(img, str(self.c["6v"]), (450, 370), font, 5, color, 5)
        cv2.putText(img, str(self.c["9v"]), (450, 520), font, 5, color, 5)
        cv2.putText(img, str(self.c["=v"]), (450, 680), font, 5, color, 5)

    @staticmethod
    def draw_calcul(img, tup_top, tup_bot, color, taille):
        # main cadre
        cv2.rectangle(img, tup_top, tup_bot, color, taille)

    @staticmethod
    def finger_detection(x, y, tuple1, tuple2, v):
        point = (x, y)
        is_in = Geometry.is_in(point, tuple1, tuple2)
        if is_in:
            return v
        else:
            return ""

    def values(self, x, y, which):
        color, taille = (123, 60, 120), 20

        if which == 1:
            v = self.finger_detection(x, y, self.c['1top'], self.c['1bom'], self.c['1v'])
            if v:
                return v, (color, taille), True
            else:
                return "nan", self.DEFAULT, False
        if which == 2:
            v = self.finger_detection(x, y, self.c['2top'], self.c['2bom'], self.c['4v'])
            if v:
                return v, (color, taille), True
            else:
                return "nan", self.DEFAULT, False
        if which == 3:
            v = self.finger_detection(x, y, self.c['3top'], self.c['3bom'], self.c['7v'])
            if v:
                return v, (color, taille), True
            else:
                return "nan", self.DEFAULT, False
        if which == 4:
            v = self.finger_detection(x, y, self.c['4top'], self.c['4bom'], self.c['+v'])
            if v:
                return v, (color, taille), True
            else:
                return "nan", self.DEFAULT, False
        if which == 5:
            v = self.finger_detection(x, y, self.c['5top'], self.c['5bom'], self.c['2v'])
            if v:
                return v, (color, taille), True
            else:
                return "nan", self.DEFAULT, False
        if which == 6:
            v = self.finger_detection(x, y, self.c['6top'], self.c['6bom'], self.c['5v'])
            if v:
                return v, (color, taille), True
            else:
                return "nan", self.DEFAULT, False
        if which == 7:
            v = self.finger_detection(x, y, self.c['7top'], self.c['7bom'], self.c['8v'])
            if v:
                return v, (color, taille), True
            else:
                return "nan", self.DEFAULT, False
        if which == 8:
            v = self.finger_detection(x, y, self.c['8top'], self.c['8bom'], self.c['0v'])
            if v == 0:
                return v, (color, taille), True
            else:
                return "nan", self.DEFAULT, False
        if which == 9:
            v = self.finger_detection(x, y, self.c['9top'], self.c['9bom'], self.c['3v'])
            if v:
                return v, (color, taille), True
            else:
                return "nan", self.DEFAULT, False
        if which == 10:
            v = self.finger_detection(x, y, self.c['10top'], self.c['10bom'], self.c['6v'])
            if v:
                return v, (color, taille), True
            else:
                return "nan", self.DEFAULT, False
        if which == 11:
            v = self.finger_detection(x, y, self.c['11top'], self.c['11bom'], self.c['9v'])
            if v:
                return v, (color, taille), True
            else:
                return "nan", self.DEFAULT, False
        if which == 12:
            v = self.finger_detection(x, y, self.c['12top'], self.c['12bom'], self.c['=v'])
            if v:
                return v, (color, taille), True
            else:
                return "nan", self.DEFAULT, False

    @staticmethod
    def do_this(fvalue, t, index):
        temp = t[index-1]
        if fvalue not in temp and len(temp) < main.speed+1:
            temp.append(fvalue)
        t = [[], [], [], [], [], [], [], [], [], [], [], [], []]
        t[index-1] = temp

    @staticmethod
    def take_it(t, index, value):
        if len(t[index-1]) >= main.speed:
            return value

    @staticmethod
    def listing(tt):

        if tt.__contains__('3'):
            index = tt.index('3')
            tt[index] = 0
            return 3

        if tt.__contains__('1'):
            index = tt.index('1')
            tt[index] = 0
            return 1

        if tt.__contains__('2'):
            index = tt.index('2')
            tt[index] = 0
            return 2

        if tt.__contains__('3'):
            index = tt.index('3')
            tt[index] = 0
            return 3
        if tt.__contains__('4'):
            index = tt.index('4')
            tt[index] = 0
            return 4
        if tt.__contains__('5'):
            index = tt.index('5')
            tt[index] = 0
            return 5
        if tt.__contains__('6'):
            index = tt.index('6')
            tt[index] = 0
            return 6

        if tt.__contains__('7'):
            index = tt.index('7')
            tt[index] = 0
            return 7
        if tt.__contains__('8'):
            index = tt.index('8')
            tt[index] = 9
            return 8
        if tt.__contains__('9'):
            index = tt.index('9')
            tt[index] = 0
            return 9

        if tt.__contains__('0'):
            index = tt.index('0')
            tt[index] = 0
            return 0
        if tt.__contains__('+'):
            index = tt.index('+')
            tt[index] = 0
            return 11
        if tt.__contains__('='):
            index = tt.index('=')
            tt[index] = 0
            return 12

    def main_loop(self):
        fvalue = int(time.time())
        value = "result"
        t, tt = [[], [], [], [], [], [], [], [], [], [], [], []], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
        my_string = ""
        while self.run:
            cool, img = self.vid.read()
            img = self.hand.detectingHand(img, draw=False)
            listcoord = self.hand.positionPoint(img, andPoint=False, draw=False)
            cv2.rectangle(img, (20, 100), (600, 700), (255, 123, 0), 1)

            color1, taille1 = self.DEFAULT
            color2, taille2 = self.DEFAULT
            color3, taille3 = self.DEFAULT
            color4, taille4 = self.DEFAULT
            color5, taille5 = self.DEFAULT
            color6, taille6 = self.DEFAULT
            color7, taille7 = self.DEFAULT
            color8, taille8 = self.DEFAULT
            color9, taille9 = self.DEFAULT
            color10, taille10 = self.DEFAULT
            color11, taille11 = self.DEFAULT
            color12, taille12 = self.DEFAULT

            if len(listcoord) != 0:
                x = listcoord[8][1]
                y = listcoord[8][2]
                # now = int(round(time.time()))
                # print(f" x  = {x} / y = {y}")
                is_it1 = self.values(x, y, 1)[2]
                is_it2 = self.values(x, y, 2)[2]
                is_it3 = self.values(x, y, 3)[2]
                is_it4 = self.values(x, y, 4)[2]
                is_it5 = self.values(x, y, 5)[2]
                is_it6 = self.values(x, y, 6)[2]
                is_it7 = self.values(x, y, 7)[2]
                is_it8 = self.values(x, y, 8)[2]
                is_it9 = self.values(x, y, 9)[2]
                is_it10 = self.values(x, y, 10)[2]
                is_it11 = self.values(x, y, 11)[2]
                is_it12 = self.values(x, y, 12)[2]

                if is_it1:
                    color1, taille1 = self.values(x, y, 1)[1]
                    self.value1 = self.values(x, y, 1)[0]
                    self.do_this(fvalue, t, 1)
                elif is_it2:
                    color2, taille2 = self.values(x, y, 2)[1]
                    self.value2 = self.values(x, y, 2)[0]
                    self.do_this(fvalue, t, 2)
                elif is_it3:
                    color3, taille3 = self.values(x, y, 3)[1]
                    self.value3 = self.values(x, y, 3)[0]
                    self.do_this(fvalue, t, 3)
                elif is_it4:
                    color4, taille4 = self.values(x, y, 4)[1]
                    self.value4 = self.values(x, y, 4)[0]
                    self.do_this(fvalue, t, 4)
                elif is_it5:
                    color5, taille5 = self.values(x, y, 5)[1]
                    self.value5 = self.values(x, y, 5)[0]
                    self.do_this(fvalue, t, 5)

                elif is_it6:
                    color6, taille6 = self.values(x, y, 6)[1]
                    self.value6 = self.values(x, y, 6)[0]
                    self.do_this(fvalue, t, 6)

                elif is_it7:
                    color7, taille7 = self.values(x, y, 7)[1]
                    self.value7 = self.values(x, y, 7)[0]
                    self.do_this(fvalue, t, 7)
                elif is_it8:
                    color8, taille8 = self.values(x, y, 8)[1]
                    self.value8 = self.values(x, y, 8)[0]
                    self.do_this(fvalue, t, 8)
                elif is_it9:
                    color9, taille9 = self.values(x, y, 9)[1]
                    self.value9 = self.values(x, y, 9)[0]
                    self.do_this(fvalue, t, 9)
                elif is_it10:
                    color10, taille10 = self.values(x, y, 10)[1]
                    self.value10 = self.values(x, y, 10)[0]
                    self.do_this(fvalue, t, 10)
                elif is_it11:
                    color11, taille11 = self.values(x, y, 11)[1]
                    self.value11 = self.values(x, y, 11)[0]
                    self.do_this(fvalue, t, 11)
                elif is_it12:
                    color12, taille12 = self.values(x, y, 12)[1]
                    self.value12 = self.values(x, y, 12)[0]
                    self.do_this(fvalue, t, 12)
                else:
                    t = [[], [], [], [], [], [], [], [], [], [], [], [], []]

            self.draw_calcul(img, self.c["1top"], self.c["1bom"], color1, taille1)
            self.draw_calcul(img, self.c["2top"], self.c["2bom"], color2, taille2)
            self.draw_calcul(img, self.c["3top"], self.c["3bom"], color3, taille3)
            self.draw_calcul(img, self.c["4top"], self.c["4bom"], color4, taille4)
            # second column
            self.draw_calcul(img, self.c["5top"], self.c["5bom"], color5, taille5)
            self.draw_calcul(img, self.c["6top"], self.c["6bom"], color6, taille6)
            self.draw_calcul(img, self.c["7top"], self.c["7bom"], color7, taille7)
            self.draw_calcul(img, self.c["8top"], self.c["8bom"], color8, taille8)
            # third
            self.draw_calcul(img, self.c["9top"], self.c["9bom"], color9, taille9)
            self.draw_calcul(img, self.c["10top"], self.c["10bom"], color10, taille10)
            self.draw_calcul(img, self.c["11top"], self.c["11bom"], color11, taille11)
            self.draw_calcul(img, self.c["12top"], self.c["12bom"], color12, taille12)
            # show the number
            if self.value1 != self.DEFAULT_VALUE:
               value = str(self.value1)
               tt[0] = self.take_it(t, 1, value)

            if self.value2 != self.DEFAULT_VALUE:
                value = str(self.value2)
                tt[1] = self.take_it(t, 2, value)
            if self.value3 != self.DEFAULT_VALUE:
                value = str(self.value3)
                tt[2] = self.take_it(t, 3, value)

            if self.value4 != self.DEFAULT_VALUE:
                value = str(self.value4)
                tt[3] = self.take_it(t, 4, value)
            if self.value5 != self.DEFAULT_VALUE:
                value = str(self.value5)
                tt[4] = self.take_it(t, 5, value)

            if self.value6 != self.DEFAULT_VALUE:
                value = str(self.value6)
                tt[5] = self.take_it(t, 6, value)

            if self.value7 != self.DEFAULT_VALUE:
                value = str(self.value7)
                tt[6] = self.take_it(t, 7, value)

            if self.value8 != self.DEFAULT_VALUE:
                value = str(self.value8)
                tt[7] = self.take_it(t, 8, value)

            if self.value9 != self.DEFAULT_VALUE:
                value = str(self.value9)
                tt[8] = self.take_it(t, 9, value)

            if self.value10 != self.DEFAULT_VALUE:
                value = str(self.value10)
                tt[9] = self.take_it(t, 10, value)

            if self.value11 != self.DEFAULT_VALUE:
                value = str(self.value11)
                tt[10] = self.take_it(t, 11, value)

            if self.value12 != self.DEFAULT_VALUE:
                value = str(self.value12)
                tt[11] = self.take_it(t, 12, value)

            # write text in windows
            cv2.putText(img, str(fvalue), (950, 120), self.font, 1, (234, 66, 7), 1)
            # cv2.putText(img, str(tt), (950, 200), self.font, 1, (234, 66, 7), 1)
            print(tt)
            print("\n *************** \n")
            self.init_value()
            fvalue = int(time.time())
            if value:
                vals = Main.listing(tt)
                if vals == 11:
                    vals = "+"
                elif vals == 12:
                    vals = "="
                if vals != None:
                    l = len(my_string)

                    if l > 0:
                        if my_string[l-1] == str(vals) or my_string[l-1] == vals:
                            my_string = my_string
                        else:
                            my_string = my_string + str(vals)
                    else:
                        my_string = my_string + str(vals)

            print("\n\n" + my_string)
            if my_string != "":
                cv2.putText(img, my_string, (950, 70), self.font, 1, (234, 66, 7), 1)
            else:
                cv2.putText(img, "resulte", (950, 70), self.font, 1, (234, 66, 7), 1)

            # show windows and draw  number
            self.draw_number(img, self.font)
            self.show(img, title="calculatrice")

if __name__ == "__main__":
    import cv2
    from handTracking import HandTracking
    from coordo import *
    from geometry import Geometry
    import time

    main = Main(dico)
    main.winsize(width=1500, height=1200)

    #print("voulez vous faire tourner le programme ??")
    c = "o"            # input("soit [o/n]")
    if c == "o":
        print("starting streaming")
        main.running()
        main.main_loop()
    else:
        print(" A plus")


import re
import numpy as np
import zlib
import os

_type_table = {
    'MET_CHAR':'i1',
    'MET_UCHAR':'u1',
    'MET_SHORT':'i2',
    'MET_USHORT':'u2',
    'MET_INT':'i4',
    'MET_UINT':'u4',
    'MET_LONG':'i8',
    'MET_ULONG':'u8',
    'MET_FLOAT':'f4',
    'MET_DOUBLE':'f8'
    }
_dtype_table = {
    'int8':'MET_CHAR',
    'uint8':'MET_UCHAR',
    'int16':'MET_SHORT',
    'uint16':'MET_USHORT',
    'int32':'MET_INT',
    'uint32':'MET_UINT',
    'int64':'MET_LONG',
    'uint64':'MET_ULONG',
    'float32':'MET_FLOAT',
    'float64':'MET_DOUBLE'
    }
def _str2array(string):
    try:
        return list(map(int, string.split()))
    except:
        try:
            return list(map(float, string.split()))
        except:
            return string
def _array2str(array):
    if isinstance(array, str):
        return array
    elif isinstance(array, int):
        return str(array)
    else:
        return ' '.join(list(map(str, array)))

def read(filename):
    header, f = read_header(filename)
    image = read_data(filename, header, f)
    return image, header

def read_header(filename):
    header = {}
    with open(filename, 'rb') as f:
        meta_regex = re.compile('(\w+) = (.+)')
        for line in f:
            line = line.decode('ascii')
            match = meta_regex.match(line)
            if match:
                header[match.group(1)] = match.group(2).rstrip('\r')
                if match.group(1) == 'ElementDataFile':
                    break;
            else:
                raise RuntimeError('Bad meta header')

    header = {key:_str2array(value) for (key, value) in header.items()} #convert string into array if possible
    return header, f

def read_data(filename, header, f):
    data_filename = header['ElementDataFile']
    if data_filename == 'LOCAL': #mha
        data = f.read()
    else: #mhd
        with open(os.path.join(os.path.dirname(filename),data_filename), 'rb') as fimage:
            data = fimage.read()
    if ('CompressedData' in header) and header['CompressedData'] == 'True': #Decompress data
        data = zlib.decompressobj().decompress(data)
    data = np.frombuffer(data,dtype=np.dtype(_type_table[header['ElementType']]))
    dim = header['DimSize']
    image = np.reshape(data,list(reversed(dim)),order='C')
    return image

_default_header = {
    'ObjectType':'Image',
    'NDims':'3',
    'BinaryData':'True',
    'BinaryDataByteOrderMSB':'False',
    #'TransformMatrix':'1 0 0 0 1 0 0 0 1',
    #'Offset':'0 0 0',
    #'CenterOfRotation':'0 0 0',
    #'AnatomicalOrientation':'???',
    'CompressedData':'True'
        }

def write(filename, image, header={}):
    h = _default_header
    h['ElementSpacing'] = np.ones(image.ndim) #default spacing
    h.update(header)
    h['NDims'] = len(image.shape)
    h['ElementType'] = _dtype_table[image.dtype.name]
    h['DimSize'] = reversed(image.shape)
    h = {key:_array2str(value) for (key, value) in h.items()} #convert array into string if possible
    filename_base, file_extension = os.path.splitext(os.path.basename(filename))
    dirname = os.path.dirname(filename)
    compress_data = h['CompressedData']=='True'
    h.pop('ElementDataFile',None) #delete
    if not compress_data and 'CompressedData' in h:
        del h['CompressedData']
    if (file_extension == '.mhd'):
        if (compress_data):
            data_filename = filename_base + '.zraw'
        else:
            data_filename = filename_base + '.raw'
    else:
        data_filename = 'LOCAL'
    data = image.tobytes()
    with open(filename, 'w') as f:
        f.write('ObjectType = '+h.pop('ObjectType')+'\n')
        f.write('NDims = '+h.pop('NDims')+'\n')
        if (compress_data):
            data = zlib.compress(data)
            h['CompressedDataSize'] = str(len(data))
        for key, value in h.items():
            f.write(key+' = '+value+'\n')
        f.write('ElementDataFile = '+data_filename+'\n')
        if data_filename == 'LOCAL':
            # reopen file in binary mode
            f.close()
            f = open(filename, 'ab')
            f.write(data)
        else:
            with open(os.path.join(dirname, data_filename), 'wb') as fdata:
                fdata.write(data)


import sys
import traceback
from typing import Callable

from PySide6.QtCore import QRunnable, Signal, QObject

class WorkerSignals(QObject):
    """
    Custom signals for runnable
    """

    finished = Signal()
    error = Signal(tuple)
    result = Signal(object)

class Worker(QRunnable):
    """
    Worker class to handle threaded calls
    """

    def __init__(self, func: Callable, *args, **kwargs):
        super().__init__()

        # Store arguments
        self.func = func
        self.args = args
        self.kwargs = kwargs

        # Store signals
        self.signals = WorkerSignals()

    def run(self) -> None:
        """
        Override run function with custom

        :return: None
        """

        try:
            # Call function
            result = self.func(*self.args, **self.kwargs)
        except:
            traceback.print_exc()
            exc_type, value = sys.exc_info()[:2]
            self.signals.error.emit((exc_type, value, traceback.format_exc()))
        else:
            self.signals.result.emit(result)
        finally:
            self.signals.finished.emit()


from typing import *
import sys

input = sys.stdin.readline

N : int = int(input())

if N == 1:
    print(0)
elif N < 3:
    print(N)
else:
    dp = [0] * (N + 1)
    dp[2] = 2
    dp[3] = 3

    for i in range(4 , N + 1):
        dp[i] = int(str(dp[i - 1] + dp[i - 2])[-1])

    print(dp[N])


import os
import xarray as xr
import iris
import numpy as np
import pandas as pd
import xarray as xr
from run_params import *
from shared_functions import get_var
import tobac
import time
from jug import TaskGenerator
import datetime as ddt
dataPath = f"/camp2e/gleung/aerograd/"
anaPath = f"/camp2e/gleung/aerograd-analysis/"

runs = ['nograd.500.norad','grad.500.norad','nograd.1000.norad','grad.1000.norad']

#Feature Detection
parameters_features={}
parameters_features['position_threshold']='weighted_diff'
parameters_features['min_distance']=0
parameters_features['sigma_threshold']=1
parameters_features['threshold']=[1,3,5] #m/s
parameters_features['n_erosion_threshold']=0
parameters_features['n_min_threshold']=10
parameters_features['PBC_flag'] = 'both'
parameters_features['vertical_coord'] = 'altitude'
#Tracking
parameters_linking={}
parameters_linking['method_linking']='predict'
parameters_linking['adaptive_stop']=0.2
parameters_linking['adaptive_step']=0.95
parameters_linking['extrapolate']=0
parameters_linking['order']=1
parameters_linking['subnetwork_size']=100
parameters_linking['memory']=0
parameters_linking['time_cell_min']=5*60
parameters_linking['method_linking']='predict'
parameters_linking['v_max']=10
parameters_linking['d_min']=2000
parameters_linking['min_h1']=0
parameters_linking['max_h1']=1000
parameters_linking['min_h2']=0
parameters_linking['max_h2']=1000
parameters_linking['PBC_flag']='both'
parameters_linking['vertical_coord'] = 'altitude'

nx = 1000
ny = 1000
@TaskGenerator
def run_tobac(run):
    paths = [f"{dataPath}{run}/{p}" for p in
             sorted(os.listdir(f"{dataPath}{run}"))
             if p.startswith('a-L-') and p.endswith('.h5')]

    lat = iris.load(paths[0],'GLAT')[0]
    lon = iris.load(paths[0],'GLON')[0]

    xs = iris.coords.DimCoord(np.arange(0,nx*dx,dx),
                              standard_name='projection_x_coordinate',
                             units = 'metre')
    ys = iris.coords.DimCoord(np.arange(0,ny*dy,dy),
                              standard_name='projection_y_coordinate',
                             units = 'metre')
    zs = iris.coords.DimCoord(np.arange(0,nz,1),
                              standard_name='model_level_number')

    lat = iris.coords.AuxCoord(lat.data,
                        standard_name='latitude',
                        units='degrees')

    lon = iris.coords.AuxCoord(lon.data,
                        standard_name='longitude',
                        units='degrees')

    altitude = iris.coords.AuxCoord(alt.values,
                        standard_name='altitude',
                        units='metre')

    times = iris.coords.DimCoord(np.arange(0,len(paths)*5,5),
                              standard_name='time',
                                               units = 'minutes since 2019-09-16 00:00:00')

    ws = iris.load(paths,'WC')

    for w,p in zip(ws, paths):
        w.add_aux_coord(iris.coords.AuxCoord(
                                pd.to_datetime(p.split('/')[-1][4:-6]),
                                standard_name='time'))

    ws = iris.cube.CubeList(ws).merge()[0]

    ws.remove_coord('time')
    ws.add_dim_coord(times,0)
    ws.add_dim_coord(zs,1)
    ws.add_dim_coord(ys,2)
    ws.add_dim_coord(xs,3)
    ws.add_aux_coord(altitude, data_dims=1)
    ws.add_aux_coord(lat, data_dims=[2,3])
    ws.add_aux_coord(lon, data_dims=[2,3])

    dxy, dt = tobac.get_spacings(ws)

    Features=tobac.feature_detection_multithreshold(ws,dxy,**parameters_features)
    Track=tobac.linking_trackpy(Features,w,dt=dt,dxy=dxy,**parameters_linking)

    Track['lifetime'] = Track.cell.map(Track.groupby('cell').time_cell.max())

    Features = Features[Features.feature.isin(Track[(Track.lifetime>=ddt.timedelta(minutes=5))].feature)]

    Features.to_hdf(f"{anaPath}{run}/tobac-out/w_features.h5",'table')

    Track.to_hdf(f"{anaPath}{run}/tobac-out/w_features_track.h5",'table')

for run in runs:
    if not os.path.exists(f"{anaPath}{run}/tobac-out/"):
        os.mkdir(f"{anaPath}{run}/tobac-out/")

    run_tobac(run)


from random import sample
from collections import defaultdict, Counter

from tqdm import tqdm

from pwaa.actionability import ActionabilityFilter

class ActionablePopularWorkflow:
    def __init__(self, top_n, sample_fraction=1.0, allow_page_load=True):
        self._top_n = top_n
        self._sample_fraction = sample_fraction
        self._allow_page_load = allow_page_load

        self._all_actions = []

    def fit(self, train_examples):
        for queries, actions in train_examples:
            self._all_actions.extend(actions)

    def predict(self, queries):
        all_elements = []
        element_to_actions = defaultdict(list)

        actionability_filter = ActionabilityFilter(queries)
        for action in tqdm(sample(self._all_actions, round(len(self._all_actions) * self._sample_fraction))):
            if not self._allow_page_load and action.page_load is not None:
                continue

            is_actionable, element = actionability_filter.is_actionable(action, return_element=True)

            if is_actionable:
                all_elements.append(element)
                element_to_actions[element].append(action)

        all_element_frequencies = Counter(all_elements)

        return [sample(element_to_actions[element], 1)[0]
                for element, _ in all_element_frequencies.most_common(self._top_n)]


# -*- coding: utf-8 -*-

from os import path

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_wtf.csrf import CSRFProtect
from flask_dropzone import Dropzone
from flask_babel import Babel
from flask_minify import Minify
from flask_ckeditor import CKEditor
from flask_qrcode import QRcode

from .libs.plugins import PluginManager
from .libs.templates import TemplateManager

db = SQLAlchemy()
csrf = CSRFProtect()
plugin_manager = PluginManager()
template_manager = TemplateManager()
ckeditor = CKEditor()
babel = Babel()

def create_app():
    app = Flask(__name__)
    app.config.from_pyfile('config.py')

    app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
    app.config['TEMPLATES_AUTO_RELOAD'] = True
    app.config['DROPZONE_ENABLE_CSRF'] = True
    app.config['DEFAULT_FOLDER'] = path.abspath(path.dirname(__file__))
    app.config['UPLOAD_FOLDER'] = path.join(
    app.config['DEFAULT_FOLDER'], 'uploads')
    app.config['BABEL_TRANSLATION_DIRECTORIES'] = 'languages'
    app.config['CKEDITOR_FILE_UPLOADER'] = '/admin/medias'
    app.config['CKEDITOR_FILE_BROWSER'] = '/admin/medias'
    app.config['CKEDITOR_PKG_TYPE'] = 'full-all'
    app.config['CKEDITOR_ENABLE_CSRF'] = True

    db.init_app(app)

    from .models import Configuration,\
        Role,\
        Ability,\
        RoleAbility,\
        User,\
        UserMeta,\
        Media,\
        MediaMeta,\
        Post,\
        Page,\
        Type,\
        Term,\
        PostTerm,\
        Model,\
        Comment,\
        Menu,\
        MenuItem

    db.create_all(app=app)

    login_manager = LoginManager()
    login_manager.blueprint_login_views = {
        'admin': '/admin/login',
        'main': '/login'
    }
    login_manager.init_app(app)

    Dropzone(app)

    csrf.init_app(app)

    babel.init_app(app)

    ckeditor.init_app(app)

    QRcode(app)

    @login_manager.user_loader
    def load_user(user_id):
        return User.query.get(user_id)

    with app.app_context():
        # blueprint for admin routes
        from .admin import routes as admin_blueprint
        app.register_blueprint(admin_blueprint.admin)

        # blueprint for non-auth routes
        from .main import routes as main_blueprint
        app.register_blueprint(main_blueprint.main)

        app.config['TEMPLATE'] = Configuration.query.filter_by(
            name='template').first().value

        if Configuration.query.filter_by(name='minify_template').first().value == 'True':
            Minify(app=app, html=True, js=True, cssless=True)
        else:
            Minify(app=app, html=False, js=False, cssless=False)

    plugin_manager.init_app(app)

    template_manager.init_app(app)

    return app


from typing import *
import sys

input = sys.stdin.readline

switch = list(input().rstrip())

count : int = 0

for i in range(len(switch)):
    if switch[i] == 'Y':
        count += 1
        for j in range(i , len(switch) , i + 1):
            if switch[j] == 'Y':
                switch[j] = 'N'
            else:
                switch[j] = 'Y'

if 'Y' not in switch:
    print(count)
else:
    print(-1)


import numpy as np
from scipy.optimize import curve_fit

import matplotlib.pyplot as plt

def frac_polynomial(z, a, b, c):
    return a / (b * (z + c) ** 3) + 2.87

distance_array = np.linspace(0, 8, 101)

data = np.loadtxt('C:\\Users\\rober\\Documents\\resonance_vs_distance_QC1.txt')
distance = data[:, 0]  # in mm
f_res = data[:, 1] #  in GHz

params, covar = curve_fit(frac_polynomial, distance, f_res, p0=[1, 0.1, -9])

a_fitted = params[0]
b_fitted = params[1]
c_fitted = params[2]
# f0_fitted = params[3]

f_res_fitted = frac_polynomial(distance_array, a_fitted, b_fitted, c_fitted)

f_res_exp = frac_polynomial(7.62, a_fitted, b_fitted, c_fitted)
print(f_res_exp)

fig, ax = plt.subplots()
ax.plot(distance, f_res, 'r*', label='measured')
ax.plot(distance_array, f_res_fitted, 'r-', label=f'fitted: f(z) = {round(a_fitted, 2)}/({round(b_fitted, 2)}*({round(c_fitted, 2)}+z)) + 2.87')  #
ax.plot(7.62, f_res_exp, 'bx', label='expected')

plt.xlabel('magnet z position (mm)')
plt.ylabel('resonance frequency (GHz)')
plt.legend()

#  plt.savefig('C:\\Users\\rober\\Documents\\t_rabi_vs_mw_amplitude_trunc.png')

plt.show()


from django.conf.urls import patterns, url

from matematica import views

urlpatterns = patterns('',
    url(r'^$', views.index, name='index'),
	url(r'^/add/$', views.add, name='add'),
	url(r'^/sub/$', views.sub, name='sub'),
	url(r'^/mcdu/$', views.mcdu, name='mcdu'),
	url(r'^/money/$', views.money, name='money')
)


import math

def mergeSortRecurSive(array):

    if len(array) <= 1:
        return array

    mid = math.ceil(len(array) / 2)
    fh = mergeSortRecurSive(array[:mid])
    sh = mergeSortRecurSive(array[mid:])

    return merge(fh, sh)

def merge(l1, l2):
    p1 = 0
    p2 = 0
    srtd = []
    while len(srtd) != len(l1) + len(l2):
        if p1 == len(l1):
            srtd.append(l2[p2])
            p2 += 1
        elif p2 == len(l2):
            srtd.append(l1[p1])
            p1 += 1
        else:
            if l1[p1] > l2[p2]:
                srtd.append(l2[p2])
                p2 += 1
            else:
                srtd.append(l1[p1])
                p1 += 1
    return srtd

#print(mergeSort([5,4,3,2,1,-3,69,27,12,8,-8,5]))


def find_missing_number_1(nums):
    for i, num in enumerate(nums):
        while num != i and num < len(nums):  # here array is missing one number and hence list out of bound will happen
            nums[num], nums[i] = num, nums[num]
            num = nums[i]
    for i, num in enumerate(nums):
        if num != i:
            return i

    return len(nums)  # if all numbs are sorted, then return the last index.

# BNetter approach
def find_missing_number(nums):
    for i, num in enumerate(nums):
        while num < len(nums) and num != nums[num]:  # Look ahead and skip already in place
            nums[num], nums[i] = num, nums[num]
            num = nums[i]

    for i, num in enumerate(nums):
        if num != i:
            return i

    return len(nums)

def main():
    print(find_missing_number([4, 0, 3, 1]))
    print(find_missing_number([8, 3, 5, 2, 4, 6, 0, 1]))

main()


# -*- coding: utf-8 -*-
#------------------------------------------------------------
# pelisalacarta - XBMC Plugin
# Canal para documaniatv.com
# http://blog.tvalacarta.info/plugin-xbmc/pelisalacarta/
#------------------------------------------------------------
import urlparse,urllib2,urllib,re
import os,sys

from core import logger
from core import config
from core import scrapertools
from core.item import Item
from servers import servertools

__channel__ = "documaniatv"
__category__ = "D"
__type__ = "generic"
__title__ = "DocumaniaTV"
__language__ = "ES"

DEBUG = config.get_setting("debug")

IMAGES_PATH = os.path.join( config.get_runtime_path(), 'resources' , 'images' , 'documaniatv' )

def isGeneric():
    return True

def mainlist(item):
    logger.info("[documaniatv.py] mainlist")

    itemlist = []
    itemlist.append( Item(channel=__channel__, action="novedades"  , title="Novedades"      , url="http://www.documaniatv.com/newvideos.html",thumbnail=os.path.join(IMAGES_PATH, 'nuevos.png')))
    itemlist.append( Item(channel=__channel__, action="categorias" , title="Por categorías" , url="http://www.documaniatv.com",thumbnail=os.path.join(IMAGES_PATH, 'tipo.png')))
    #itemlist.append( Item(channel=__channel__, action="tags" , title="Por tags" , url="http://www.documaniatv.com",thumbnail=os.path.join(IMAGES_PATH, 'tipo.png')))
    itemlist.append( Item(channel=__channel__, action="novedades"  , title="Top"      , url="http://www.documaniatv.com/topvideos.html",thumbnail=os.path.join(IMAGES_PATH, 'nuevos.png')))
    itemlist.append( Item(channel=__channel__, action="canales" , title="Por canales" , url="http://www.documaniatv.com",thumbnail=os.path.join(IMAGES_PATH, 'tipo.png')))
    itemlist.append( Item(channel=__channel__, action="viendo" , title="Viendo ahora" , url="http://www.documaniatv.com",thumbnail=os.path.join(IMAGES_PATH, 'tipo.png')))

    itemlist.append( Item(channel=__channel__, action="search"     , title="Buscar"         , thumbnail=os.path.join(IMAGES_PATH, 'search_icon.png')))
    return itemlist

def novedades(item):
    logger.info("[documaniatv.py] novedades")
    itemlist = []

    # Descarga la pagina
    data = scrapertools.cache_page(item.url)
    logger.info(data)
    matches = re.compile('<li[^<]+
(.*?)
',re.DOTALL).findall(data)

    for match in matches:
        logger.info(str(match))
        try:

            #logger.info(match)
            scrapedtitle = scrapertools.get_match(match,'title="(.*?)"')
            #logger.info("scrapedtitle")
            #logger.info(scrapedtitle)
            scrapedurl = scrapertools.get_match(match,'<a href="(.*?)"')
            #logger.info(scrapedurl)
            scrapedthumbnail = scrapertools.get_match(match,'<img src="(.*?)"')
            #logger.info(scrapedthumbnail)
            scrapedplot = scrapertools.get_match(match,'
(.*?)
')
            #scrapedplot = scrapertools.htmlclean(scrapedplot)
            scrapedplot = scrapertools.entityunescape(scrapedplot)
            #logger.info(scrapedplot)
            if (DEBUG): logger.info("title=["+scrapedtitle+"], url=["+scrapedurl+"], thumbnail=["+scrapedthumbnail+"]")

            itemlist.append( Item(channel=__channel__, action="play", title=scrapedtitle , url=scrapedurl , thumbnail=scrapedthumbnail , plot=scrapedplot , fanart=scrapedthumbnail, folder=False) )

        except:
            logger.info("documaniatv.novedades Error al añadir entrada "+match)
            pass

    # Busca enlaces de paginas siguientes...
    try:
        next_page_url = scrapertools.get_match(data,'<li class="active"[^<]+<a[^<]+</a[^<]+</li[^<]+<li[^<]+')
        next_page_url = urlparse.urljoin(item.url,next_page_url)
        itemlist.append( Item(channel=__channel__, action="novedades", title=">> Pagina siguiente" , url=next_page_url , thumbnail="" , plot="" , folder=True) )
    except:
        logger.info("documaniatv.novedades Siguiente pagina no encontrada")

    return itemlist

def categorias(item):
    logger.info("[documaniatv.py] categorias")
    itemlist = []

    data = scrapertools.cache_page(item.url)

    # Saca el bloque con las categorias
    data = scrapertools.get_match(data,"""Categorias (.*?)

""")

    #
    patron = '<li[^<]+]+>([^<]+)'
    matches = re.compile(patron,re.DOTALL).findall(data)
    for match in matches:
        #itemlist.append( Item(channel=__channel__ , action="novedades" , title=match[1],url="http://www.documaniatv.com"+match[0]))
        itemlist.append( Item(channel=__channel__ , action="novedades" , title=match[1],url=match[0]))

    return itemlist

def tags(item):
    logger.info("[documaniatv.py] categorias")
    itemlist = []

    # Saca el bloque con las categorias
    data = scrapertools.cache_page(item.url)
    data = scrapertools.get_match(data,'
Palabras Clave
(.*?)
')

    #
    patron = ']+>([^<]+)'
    matches = re.compile(patron,re.DOTALL).findall(data)
    for match in matches:
        itemlist.append( Item(channel=__channel__ , action="novedades" , title=match[1],url="http://www.documaniatv.com"+match[0]))

    return itemlist

def canales(item):
    logger.info("[documaniatv.py] canales")
    itemlist = []

    # Saca el bloque con las categorias
    data = scrapertools.cache_page(item.url)
    #logger.info(data)
    data = scrapertools.get_match(data,"""Canales(.*?)

""")
    #logger.error(data)

    #
    patron = '<li[^<]+]+>([^<]+)'
    matches = re.compile(patron,re.DOTALL).findall(data)
    for match in matches:
        #itemlist.append( Item(channel=__channel__ , action="novedades" , title=match[1],url="http://www.documaniatv.com"+match[0]))
        itemlist.append( Item(channel=__channel__ , action="novedades" , title=match[1],url=match[0]))

    return itemlist

def viendo(item):
    logger.info("[documaniatv.py] viendo")
    itemlist = []

    # Saca el bloque con las categorias
    data = scrapertools.cache_page(item.url)
    logger.info(data)
    data = scrapertools.get_match(data,"""
(.*?)
""")

    #
    patron = ']+>([^<]+)'
    matches = re.compile(patron,re.DOTALL).findall(data)
    imgs= ']+>'
    matc = re.compile(imgs,re.DOTALL).findall(data)

    for match,m in zip(matches,matc):
        logger.error(str(match))
        itemlist.append( Item(channel=__channel__ , action="play" , title=match[1],url=match[0],thumbnail=m))

    return itemlist

def search(item,texto):
    #http://www.documaniatv.com/search.php?keywords=luna&btn=Buscar
    logger.info("[documaniatv.py] search")
    if item.url=="":
        item.url="http://www.documaniatv.com/search.php?keywords=%s"
    texto = texto.replace(" ","+")
    item.url = item.url % texto
    try:
        return novedades(item)
    # Se captura la excepción, para no interrumpir al buscador global si un canal falla
    except:
        import sys
        for line in sys.exc_info():
            logger.error( "%s" % line )
        return []

def play(item):
    logger.info("documaniatv.play")
    itemlist = []

    # Descarga la pagina
    data1 = scrapertools.cachePage(item.url)
    logger.info(data1)
    patron= 'itemprop="embedURL" content="(.*?)"'
    matc = re.compile(patron,re.DOTALL).findall(data1)
    logger.info(matc[0])

    data = scrapertools.cachePage(matc[0])
    logger.info(data)

    # Busca los enlaces a los videos
    video_itemlist = servertools.find_video_items(data=data)
    for video_item in video_itemlist:
        itemlist.append( Item(channel=__channel__ , action="play" , server=video_item.server, title=item.title+video_item.title,url=video_item.url, thumbnail=video_item.thumbnail, plot=video_item.plot, folder=False))

    # Extrae los enlaces a los videos (Directo)
    patronvideos = "src= '([^']+)'"
    matches = re.compile(patronvideos,re.DOTALL).findall(data)
    if len(matches)>0:
        if not "www.youtube" in matches[0]:
            itemlist.append( Item(channel=__channel__ , action="play" , server="Directo", title=item.title+" [directo]",url=matches[0], thumbnail=item.thumbnail, plot=item.plot))

    return itemlist

# Verificación automática de canales: Esta función debe devolver "True" si está ok el canal.
def test():
    from servers import servertools
    # mainlist
    mainlist_items = mainlist(Item())
    # Da por bueno el canal si alguno de los vídeos de "Novedades" devuelve mirrors
    items = novedades(mainlist_items[0])
    bien = False
    for singleitem in items:
        mirrors = servertools.find_video_items( item=singleitem )
        if len(mirrors)>0:
            bien = True
            break

    return bien


"""
Server-Client Chat System using TCP
"""

import asyncio
import argparse
import struct
from datetime import datetime

async def recv_formatted_data(reader, frmt):
    """
    Receives struct-formatted data from the given reader according to the struct format given and
    returns a tuple of values.
    """
    data = await reader.read(struct.calcsize(frmt))
    return struct.unpack(frmt, data)

async def recv_single_value(reader, frmt):
    """
    Receives a single value from the given reader according to the struct format given and returns
    it.
    """
    single_value = await recv_formatted_data(reader, frmt)
    return single_value[0]

async def recv_str(reader):
    """
    Receives a string using the reader. The string must be prefixed with its length and encoded.
    """
    length = await recv_single_value(reader, '<i')
    data = await reader.read(length)
    return data.decode()

async def recv_str_list(reader):
    """
    Receives a list of strings from the reader. The list is prefixed with the length and each string
    is prefixed with recv_str().
    """
    length = await recv_single_value(reader, "<i")
    return [await recv_str(reader) for _ in range(length)]

def send_single_value(writer, frmt, value):
    """
    Sends a single value using the given writer according to the struct format given.
    """
    writer.write(struct.pack(frmt, value))

def send_str(writer, string):
    """
    Sends a string using the given writer. The string is encoded then prefixed with the length as a 4-byte
    integer.
    """
    string = string.encode()
    data = struct.pack('<i', len(string))
    data += struct.pack(str(len(string)) + 's', string)
    writer.write(data)

def send_str_list(writer, strings):
    """
    Sends a list of strings using given writer. The list is prefixed with its length as a 4-byte
    integer. Each string is send with send_str().
    """
    length = struct.pack('<i', len(strings))
    writer.write(length)
    for string in strings:
        send_str(writer, string)

class ClientTCP():
    """
    Client that can connect to TCP chat system server. Clients provide a username, and upon a successful
    connection, they receive recent chat history. Clients are free to chat with others on the server.
    """

    async def start_chatting(self, reader, writer):
        """
        Connect to the server at addr:port and start chatting in chatroom.
        """

        # Initial communication to the server, saying that you are about to send your username
        # to check if it is valid
        send_single_value(writer, '<i', 471)
        await writer.drain()

        # Client sends a string, being their username for the chat room, to the server
        username = input('Enter your username for the chatroom: ')
        send_str(writer, username)
        await writer.drain()

        # If false is received, connection is aborted
        try:
            status = await recv_single_value(reader, '<?')
            if status == False:
                writer.close()
                return
        # If anything goes wrong in Entering username (?)
        except (KeyboardInterrupt, Exception):
            print('Exception during Entering username')
            writer.close()
            return

        # Receive an integer and if it follows protocol, receive the history list
        response = await recv_single_value(reader, '<i')
        if response == 371:
            past_messages_num = await recv_single_value(reader, '<i')
            if past_messages_num == 0:
                pass
            else:
                for message in range(past_messages_num):
                    past_message = await recv_str_list(reader)
                    message_data = '%s %s: %s' % (past_message[0], past_message[1], past_message[2])
                    print(message_data)
        return username

    async def send_message(self, writer, username):
        """
        Send a message in the chatroom. Message is sent to the server
        to handle sending it to all clients.
        """
        loop = asyncio.get_running_loop()
        while True:
            message = await loop.run_in_executor(None, input)
            local_time = datetime.now()
            str_time = local_time.strftime("%H:%M:%S")
            # Client disconnects when 'enter' is pressed in the chat
            if not message:
                print('You left the chatroom. Goodbye.')
                return
            send_single_value(writer, '<i', 472)
            send_str_list(writer, [str_time, username, message])
            await writer.drain()

    async def recv_new_message(self, reader):
        """
        Receive a new message from the server that was sent from any client in the chatroom.
        """
        while True:
            try:
                new_message_response = await recv_single_value(reader, '<i')
            except Exception:
                break
            else:
                if new_message_response == 372:
                    message = await recv_str_list(reader)
                    # Print out the received message.
                    print(message[0], message[1] + ':', message[2])

    async def connect_to_server(self):
        """
        Open a connection to the server and attempt to start chatting in the
        chatroom, and sending/receiving new messages.
        """
        reader, writer = await asyncio.open_connection('googlecloud.cslab.moravian.edu', 42069)

        valid_username = await self.start_chatting(reader, writer)
        if not valid_username:
            return

        sending_messages = asyncio.create_task(self.send_message(writer, valid_username))
        receiving_messages = asyncio.create_task(self.recv_new_message(reader))

        await sending_messages
        receiving_messages.cancel()
        try:
            await receiving_messages
        except asyncio.exceptions.CancelledError:
            pass

class ServerTCP():
    """
    TCP Chat System Server that listens for and accepts incoming connections from any number of simultaneous clients.
    New clients are provided recent chat history (up to 10 messages) and are free to chat with other clients.
    """

    def __init__(self):
        """
        Python constructor that initializes lists of writers, current usernames,
        and recent messages.
        """
        self.WRITERS = [] # list of all writers currently connected
        self.USERNAMES_LIST = []  # contains tuples, each one containing the time, username and message
        self.RECENT_MESSAGES = [] # up to 10 recent chat messages

    def get_history(self):
        """
        Getter method for the chat history.
        """
        return self.RECENT_MESSAGES

    def update_history(self, time,username,message):
        """
        This method has to add the three stings (username, time, text) to the table and remove the oldest one.
        """
        new_message = (time, username, message)
        if len(self.RECENT_MESSAGES) >= 10:
            self.RECENT_MESSAGES.pop(0)
        self.RECENT_MESSAGES.append(new_message)

    def send_history(self, writer, history):
        """
        Send the chat history to a new client connecting
        to the chatroom.
        """
        send_single_value(writer, '<i', 371)
        send_single_value(writer, '<i', len(history))
        if len(history) != 0:
            for message in history:
                send_str_list(writer, message)

    def send_new_message(self, message_info):
        """
        When a new message is received from a client, this function is called to
        send the message to all clients.
        """
        for writer in self.WRITERS:
            try:
                send_single_value(writer, '<i', 372)
            except KeyboardInterrupt:
                pass
            else:
                send_str_list(writer, message_info)

    async def run_server(self):
        """
        Start the server running on the given port. The server accepts clients, handles their requests,
        then goes back to accept another client (i.e. it has an infinite loop). To handle requests this
        calls the server_handle_request() function. If a client causes a problem it is reported then the
        server keeps going (i.e. if server_handle_request() raises any exception it is printed and then
        the next client is accepted).
        """
        server = await asyncio.start_server(self.server_handle_request, '', 42069)
        async with server:
            await server.serve_forever()

    async def server_handle_request(self, reader, writer):
        """
        Handles client requests using the reader and writer that were initialized
        from asyncio.start_server().
        """
        # Add new writer to the list of writers
        self.WRITERS.append(writer)
        user_info = ()
        while True:
            try:
                protocol_num = await recv_single_value(reader, '<i')
            except struct.error:
                break
            # New client is joining the chat room
            if protocol_num == 471:
                client_username = await recv_str(reader)
                for client_id in self.USERNAMES_LIST:
                    if client_username == client_id[1]:
                        send_single_value(writer, '<?', False)
                        self.WRITERS.remove(writer)
                        await writer.drain()
                        writer.close()  # close
                        return
                # Send True to the client to confirm that the name is available
                send_single_value(writer, '<?', True)
                await writer.drain()
                # Add new user to the list of active users
                addr = writer.get_extra_info('peername')[0]
                new_user = (addr, client_username)  # address, username
                user_info = new_user
                self.USERNAMES_LIST.append(new_user)
                # Send message history to the new client
                self.send_history(writer, self.get_history())
                await writer.drain()
            # The client wants to send a new message in the chatroom
            if protocol_num == 472:
                try:
                    # Get the list of username, time and message
                    message_info = await recv_str_list(reader)
                except (KeyboardInterrupt, Exception):
                    break
                # Send the message to all clients, and update the history
                else:
                    self.send_new_message(message_info)
                    await writer.drain()
                    time = message_info[0]
                    username = message_info[1]
                    message = message_info[2]
                    self.update_history(time, username, message)
        # Remove user and writer from lists, and close the writer
        self.USERNAMES_LIST.remove(user_info)
        self.WRITERS.remove(writer)
        writer.close()

async def main():
    """
    The main function uses an Argument Parser to get the command-line arguments and then calls ones
    of the functions: run_server() or connect_to_server().
    """
    parser = argparse.ArgumentParser(description='Interact with TCP client-server chatroom')
    subparsers = parser.add_subparsers(title='command', dest='cmd', required=True)
    subparsers.add_parser(name='server', description='run the server')
    subparsers.add_parser(name='client', description='connect to chatroom as a client')
    args = parser.parse_args()
    if args.cmd == 'server':
        server = ServerTCP()
        await server.run_server()
    if args.cmd == 'client':
        client = ClientTCP()
        await client.connect_to_server()

asyncio.run(main())


import sys
sys_input=sys.stdin.readline

k,l=map(int,sys_input().rstrip().split())
waiting=dict()

for i in range(l):
    user=sys_input().rstrip()

    waiting[user]=i

ans=list(sorted(waiting,key=lambda x:waiting[x]))

for i in range(min(k,len(ans))):
    print(ans[i])


from scipy.ndimage import grey_opening, grey_closing
from skimage import io
import matplotlib.pyplot as plt
import numpy as np
from timeit import default_timer as timer

def square_closing(img, db, bb):
    return grey_closing(grey_opening(img, structure=db), structure=bb)

def square_opening(img, db, bb):
    return grey_opening(grey_closing(img, structure=db), structure=bb)

def top_hat(img, db, bb):
    NWTH = img - np.minimum(img, square_closing(img, db, bb))
    NBTH = np.maximum(img, square_opening(img, db, bb)) - img

    return [NWTH, NBTH]

def multiscale_top_hat(img, nw, nl, nm, ns, n):
    NWTH_out = np.zeros_like(img)
    NBTH_out = np.zeros_like(img)

    for i in range(n):
        bb = np.zeros([nl + ns * i, nl + ns * i])
        db = np.pad(np.zeros([nw + ns * i, nw + ns * i]), [nm, nm])

        single_scale_top_hat = top_hat(img, db, bb)
        NWTH_out = np.maximum(NWTH_out, single_scale_top_hat[0])
        NBTH_out = np.maximum(NBTH_out, single_scale_top_hat[1])

    return [NWTH_out, NBTH_out]

if __name__ == '__main__':
    image = io.imread('01.jpg')
    image = np.array(image[:, :, 0]).astype(int)

    ax = plt.hist(image.ravel(), bins=256)
    plt.show()

    plt.imshow(image, cmap='gray', vmin=0, vmax=255)
    plt.show()
    nW = 5
    nL = 5
    nM = 2
    nS = 11
    n = 9

    start = timer()
    [NWTH, NBTH] = multiscale_top_hat(image, nW, nL, nM, nS, n)
    end = timer()

    print(end - start)
    out = image * 0.2 + 5 * NWTH - 3 * NBTH
    out[out > 255] = 255
    out[out < 0] = 0

    ax = plt.hist(out.ravel(), bins=256)
    plt.show()
    plt.imshow(out, cmap='gray', vmin=0, vmax=255)
    plt.show()


import os
import numpy as np
import PIL
from PIL import Image
from torch.utils.data import Dataset
from torchvision import transforms
import torch
import random
import h5py
import json

class VGDatabase(Dataset):
    def __init__(self, vocab, h5_path, image_dir, image_size=256, max_objects=10, max_samples=None,
                 include_relationships=True, use_orphaned_objects=True):
        with open(vocab, 'r') as f:
            vocab = json.load(f)
        self.image_dir = image_dir
        self.image_size = (image_size, image_size)
        self.vocab = vocab
        self.num_objects = len(vocab['object_idx_to_name'])
        self.use_orphaned_objects = use_orphaned_objects
        self.max_objects = max_objects
        self.max_samples = max_samples
        self.include_relationships = include_relationships

        transform = [Resize(self.image_size), transforms.ToTensor()] # augmentation
        self.transform = transforms.Compose(transform)

        self.data = {}
        with h5py.File(h5_path, 'r') as f:
            for k, v in f.items():
                if k == 'image_paths':
                    self.image_paths = list(v)
                else:
                    self.data[k] = torch.IntTensor(np.asarray(v))

    def __len__(self):
        num = self.data['object_names'].size(0)
        if self.max_samples is not None:
            return min(self.max_samples, num)
        return num

    def __getitem__(self, index):
        img_path = os.path.join(self.image_dir, str(self.image_paths[index], encoding="utf-8"))

        with open(img_path, 'rb') as f:
            with PIL.Image.open(f) as image:
                WW, HH = image.size
                image = self.transform(image.convert('RGB'))

        image = image * 2 - 1

        obj_idxs_with_rels = set()
        obj_idxs_without_rels = set(range(self.data['objects_per_image'][index].item()))
        for r_idx in range(self.data['relationships_per_image'][index]):
            s = self.data['relationship_subjects'][index, r_idx].item()
            o = self.data['relationship_objects'][index, r_idx].item()
            obj_idxs_with_rels.add(s)
            obj_idxs_with_rels.add(o)
            obj_idxs_without_rels.discard(s)
            obj_idxs_without_rels.discard(o)

        obj_idxs = list(obj_idxs_with_rels)
        obj_idxs_without_rels = list(obj_idxs_without_rels)
        if len(obj_idxs) > self.max_objects - 1:
            obj_idxs = random.sample(obj_idxs, self.max_objects)
        if len(obj_idxs) < self.max_objects - 1 and self.use_orphaned_objects:
            num_to_add = self.max_objects - 1 - len(obj_idxs)
            num_to_add = min(num_to_add, len(obj_idxs_without_rels))
            obj_idxs += random.sample(obj_idxs_without_rels, num_to_add)
        O = len(obj_idxs) + 1

        objs = torch.LongTensor(O).fill_(-1)

        boxes = torch.FloatTensor([[0, 0, 1, 1]]).repeat(O, 1)
        obj_idx_mapping = {}
        for i, obj_idx in enumerate(obj_idxs):
            objs[i] = self.data['object_names'][index, obj_idx].item()
            x, y, w, h = self.data['object_boxes'][index, obj_idx].tolist()
            x0 = float(x) / WW
            y0 = float(y) / HH
            x1 = float(x + w) / WW
            y1 = float(y + h) / HH
            boxes[i] = torch.FloatTensor([x0, y0, x1, y1])
            obj_idx_mapping[obj_idx] = i

        objs[O - 1] = self.vocab['object_name_to_idx']['__image__']

        triples = []
        for r_idx in range(self.data['relationships_per_image'][index].item()):
            if not self.include_relationships:
                break
            s = self.data['relationship_subjects'][index, r_idx].item()
            p = self.data['relationship_predicates'][index, r_idx].item()
            o = self.data['relationship_objects'][index, r_idx].item()
            s = obj_idx_mapping.get(s, None)
            o = obj_idx_mapping.get(o, None)
            if s is not None and o is not None:
                triples.append([s, p, o])

        in_image = self.vocab['pred_name_to_idx']['__in_image__']
        for i in range(O - 1):
            triples.append([i, in_image, O - 1])

        triples = torch.LongTensor(triples)
        return image, objs, boxes, triples

class VGTrain(VGDatabase):
    def __init__(self, vocab, h5_path, image_dir, **kwargs):
        super().__init__(vocab=vocab, h5_path=h5_path, image_dir=image_dir, **kwargs)

class VGValidation(VGDatabase):
    def __init__(self, vocab, h5_path, image_dir, **kwargs):
        super().__init__(vocab=vocab, h5_path=h5_path, image_dir=image_dir, **kwargs)

def vg_collate_fn(batch):
    all_imgs, all_objs, all_boxes, all_triples = [], [], [], []
    all_obj_to_img, all_triple_to_img = [], []
    obj_offset = 0
    for i, (img, objs, boxes, triples) in enumerate(batch):
        all_imgs.append(img[None])
        O, T = objs.size(0), triples.size(0)
        all_objs.append(objs)
        all_boxes.append(boxes)
        triples = triples.clone()
        triples[:, 0] += obj_offset
        triples[:, 2] += obj_offset
        all_triples.append(triples)

        all_obj_to_img.append(torch.LongTensor(O).fill_(i))
        all_triple_to_img.append(torch.LongTensor(T).fill_(i))
        obj_offset += O

    all_imgs = torch.cat(all_imgs)
    all_objs = torch.cat(all_objs)
    all_boxes = torch.cat(all_boxes)
    all_triples = torch.cat(all_triples)
    all_obj_to_img = torch.cat(all_obj_to_img)
    all_triple_to_img = torch.cat(all_triple_to_img)

    out = (all_imgs, all_objs, all_boxes, all_triples, all_obj_to_img, all_triple_to_img)
    return out

class Resize(object):
    def __init__(self, size, interp=PIL.Image.BILINEAR):
        if isinstance(size, tuple):
            H, W = size
            self.size = (W, H)
        else:
            self.size = (size, size)
        self.interp = interp

    def __call__(self, img):
        return img.resize(self.size, self.interp)


# Sales by Match
# https://www.hackerrank.com/challenges/sock-merchant
# Matching pairs of socks and totaling the number of pairs

n = 9                                                           # Length of the array
ar = [1,1,3,1,2,1,3,3,3]                                        # Individual socks

def find_pairs(n, ar):
    x = ar[0]                                                   # Sample number
    for item in range(1, n):                                    # Test objects in array to match the sample
        if ar[item] == x:                                       # If a match exists, remove the sample and the test object from the array and return a match
            del ar[item]
            del ar[0]
            return 1
        elif x not in ar[1:]:                                   # If there is no match remove the sample object and return no match
            del ar[0]
            return 0

def checkIfDuplicates(listOfElems):                             # Check if there are any duplicates
    if len(listOfElems) == len(set(listOfElems)):               # Run a generic check to see if there are any matches at all
        return False
    else:
        return True

def sockMerchant(n, ar):
    total_pairs = 0
    while True:
        if n == 0:                                              # If the array is empty, return no pairs
            return total_pairs
        else:                                                   # If there are items in the array, continue
            result = checkIfDuplicates(ar)                      # Check if there are any duplicates at all
            if result == True:                                  # If so, call the find_pairs function to calculate the number of pairs
                total_pairs += find_pairs(n,ar)
            else:                                               # If not, return no pairs
                return total_pairs

print(sockMerchant(n, ar))


import numpy as np
import pandas as pd
import random
import itertools

# make your random same as mine

def load_dataset():
    '''
    Load the train examples and labels from .csv file

    Arguments:
    rtype x: ndarray, (9, 49)
    rtype y: ndarray, (1, 49)
    '''
    train = pd.read_csv(filepath_or_buffer='training-set.csv', header=None).values
    x, y = train[:, :9].T, train[:, -1:].T
    return x, y

def initialize_generation():
    '''
    Initializa W and Theta
    W[i]: uniformly distributed in (0, 1)
    Theta[i]: "standard norm" + 2.25

    Arguments:
    rtype W: List[ndarray], len(W) == 100, W[i].shape = (1, 9)
    rtype Theta: List[float], len(Theta) == 100
    '''
    W = [np.random.uniform(-1, 1, (1, 9)) for _ in range(100)]
    Theta = [float(np.random.randn(1) + 2.25) for _ in range(100)]
    return W, Theta

def predict(x, y, w, theta):
    '''
    Calculate the accuracy of a certain w and theta

    Arguments:
    type x: ndarray, (9, 49)
    type y: ndarray, (1, 49)
    type w: ndarray, (1, 9)
    type theta: float

    rtype acc: float
    '''
    predict = np.matmul(w, x)

    # greater than theta, predict = 1, less than theta, predict = 0
    predict = np.where(predict > theta, 1, 0)

    acc = np.sum(predict == y) / y.shape[1]
    return acc

def random_choose(x, y, W, Theta, k, l):
    '''
    randomly select k elements out of the l
    then pick the element with highest predict accuracy among the selected k elements

    Arguments:
    type x: ndarray, shape(9, 49)
    type y: ndarray, shape(1, 49)
    type W: List[ndarray]
    type Theta: List[float]
    type k: int
    type l: List

    rtype: int
    '''
    random.shuffle(l)
    picked_index = l[:k]
    highest_acc, target_index = -1, -1
    for index in picked_index:
        acc = predict(x, y, W[index], Theta[index])
        if acc > highest_acc:
            highest_acc, target_index = acc, index
    return target_index

def mutation(w, theta):
    new_w = np.array(w)
    index = random.randint(0, w.shape[1] - 1)

    for i in range(w.shape[1]):
        if random.randint(1, 100) < 15:
            new_w[0, i] = float(np.random.uniform(-1, 1, 1))
    if random.randint(1, 100) < 50:
        theta = theta + float(np.random.randn(1))
    return new_w, theta

def update_parameters(x, y, W, Theta):
    tmp = list(range(100))
    new_W, new_Theta = [], []

    for _ in range(100):
        # copy operation, first 10 elements
        if _ < 10:
            index = random_choose(x, y, W, Theta, 7, tmp)
            w, theta = mutation(W[index], Theta[index])
            new_W.append(w); new_Theta.append(theta)

        # cross over operation, next 90 elements
        else:
            # choose the first parent for cross over
            index_1 = random_choose(x, y, W, Theta, 7, tmp)
            parent1_W = np.array(W[index_1])

            # choose the second parent for cross over
            tmp.remove(index_1)
            index_2 = random_choose(x, y, W, Theta, 7, tmp)
            parent2_W = np.array(W[index_2])
            tmp.append(index_1)

            # cross over and get the next generation
            i = random.randint(1, 10)
            parent1_W, parent2_W = np.concatenate([parent1_W[:, :i], parent2_W[:, i:]], axis=1), np.concatenate([parent2_W[:, :i], parent1_W[:, i:]],axis=1)

            if predict(x, y, parent1_W, Theta[index_2]) > predict(x, y, parent2_W, Theta[index_1]):
                w, theta = mutation(parent1_W, Theta[index_2])
                new_W.append(w); new_Theta.append(theta)
            else:
                w, theta = mutation(parent2_W, Theta[index_1])
                new_W.append(w); new_Theta.append(theta)

    return new_W, new_Theta

x, y = load_dataset()
W, Theta = initialize_generation()

for _ in range(500):
    highest_acc, target_index = -1, -1
    for i in range(100):
        acc = predict(x, y, W[i], Theta[i])
        if highest_acc < acc:
            highest_acc, target_index = acc, i
    W, Theta = update_parameters(x, y, W, Theta)

    print("iteration: {}, accuracy: {}%".format(_, highest_acc * 100))
    if highest_acc > 0.98:
        print(W[target_index], Theta[target_index])
        input()
        break
# update_parameters(x, y, W, Theta)

# print(len(W), W[1].shape, len(Theta), Theta[1])