#!/usr/local/bin/python3
# -*- coding: utf-8 -*-

import tkinter as tk
import tkinter.messagebox as msgbox
import tkinter.filedialog as filebox
from tkSimpleDialog import Dialog

from ScrabbleGame import ScrabbleGame, _readLines
from math import cos, sin, pi

import platform
import os

#print(tk.TclVersion, tk.TkVersion)

# -- program directory (../from here)
filedir = os.path.split(__file__)[0]
PROGDIR = os.path.split(filedir)[0]

style = {}
print(platform.system())
if platform.system() == "Windows":
    font = "Monaco"
    style["coordinates"] = (font, -11)
    style["msgbox"] = (font, -11)
    style["letter"] = (font, -15, "bold")
    style["letter2"] = (font, -8)
    style["remainder"] = (font, -10)
    style["joker"] = ("Comic Sans Ms", -18, "bold")

    keyOPT = "Control"

else:
    font = "Monaco"
    style["coordinates"] = (font, 11)
    style["msgbox"] = (font, 11)
    style["letter"] = (font, 15)
    style["letter2"] = (font, 8)
    style["remainder"] = (font, 10)
    style["joker"] = ("Comic Sans Ms", -18, "bold")

    keyOPT = "Command"

style["bg"] = "#93AAAA"

# ShortCuts
SCNEW = "<{}-n>".format(keyOPT)
SCSAVE = "<{}-s>".format(keyOPT)
SCSAVEAS = "<{}-S>".format(keyOPT)
SCOPEN = "<{}-o>".format(keyOPT)
SCQUIT = "<{}-q>".format(keyOPT)

SCPICK = "<{}-t>".format(keyOPT)
SCPICK2 = "<{}-T>".format(keyOPT)
SCFIND = "<{}-f>".format(keyOPT)
SCPLAY = "<{}-p>".format(keyOPT)

class Application(tk.Tk):
    def __init__(self):
        tk.Tk.__init__(self)
        self.lift()
        self.title("Scrabble Solver -- X. Morin -- 01/2018")
        self.resizable(False, False)
        p = tk.PanedWindow(self, orient=tk.HORIZONTAL, bd=0, bg=style["bg"], sashwidth=0)
        p.pack()
        self.canvas = self.LeftRegion(p)
        self.rightRegion = self.RightRegion(p)
        p.add(self.canvas)
        p.add(self.rightRegion)
        p.add(tk.Canvas(width=7, bg=style["bg"], bd=0, borderwidth=0, highlightthickness=0))
        self.config(menu=self.MenuBar(self))

        self.game = None
        self.fileName = None
        self.fileHistory = ""

        self.readConfig()

        self.newGame()

        # Shortcuts
        self.bind(SCNEW, self.newGame)
        self.bind(SCSAVE, self.saveGame)
        self.bind(SCSAVEAS, self.saveAs)
        self.bind(SCOPEN, self.loadGame)
        self.bind(SCQUIT, self.quitApp)

        self.bind(SCPICK, self.pickLettersBox)
        self.bind(SCPICK2, self.pickLetters2)
        self.bind(SCFIND, self.findSolution)
        self.bind(SCPLAY, self.playWordBox)

    def readConfig(self, fileName = "default.config"):
        """Load configuration from fileName"""
        Lines = _readLines(fileName)
        return (Lines[0], Lines[1], int(Lines[2]))

    def saveConfig(self):
        """Writes in default.config the current configuration"""
        file = open("default.config", "w")
        file.write(self.lettersFile + '\n')
        file.write(self.dictionaryFile + '\n')
        file.write(str(self.nbLetters))
        file.close()

    def setConfig(self, lettersFile, dictionaryFile, nbLetters):
        """Set the configuration"""
        self.lettersFile = lettersFile
        self.dictionaryFile = dictionaryFile
        self.nbLetters = int(nbLetters) # just in case

    def quitApp(self, event = None):
        print("quitSC")
        self.quit()
        self.destroy()

    def newGame(self, event = None, configuration = None):
        test = True
        if self.game is not None and len(self.fileHistory)>0:
            test = msgbox.askyesno("La sauvegarde n'est pas automatique",
                                   "Êtes-vous sûr de bien vouloir quitter la partie en cours ?",
                                   icon=msgbox.WARNING)

        if configuration is None:
                configuration = self.readConfig()

        self.lettersFile = configuration[0]
        self.dictionaryFile = configuration[1]
        self.nbLetters = configuration[2]

        if test:
            self.game = ScrabbleGame(self.lettersFile, self.dictionaryFile, self.nbLetters)
            self.canvas.game = self.game
            self.canvas.Refresh()
            self.rightRegion.clean()
            self.fileHistory = ""

    def loadGame(self, event = None):
        """Loads a game in txt file.
        Format is : rackActions, wordPlayed, position."""
        self.fileName = filebox.askopenfilename(title = "Ouvrir une partie...", initialdir=self._gamesDir(), filetypes=[("SCR", "*.scr")])
        if self.fileName != '':
            self.newGame(configuration=self.readConfig(self.fileName))
            Lines = _readLines(self.fileName)[3:]
            for line in Lines:
                if line == "":
                    return
                t = line.split(':')
                t2 = t[0].split(' ')
                for letters in t2:
                    if letters != '':
                        self.pickLetters(letters)
                if len(t)>1:
                    self.playWord(t[-2],t[-1])
        else:
            self.fileName = None

    def saveGame(self, event = None):
        """Dialog box and action to save game."""
        if self.fileName is None:
            s = filebox.asksaveasfilename(initialdir=self._gamesDir(),
                                          defaultextension = ".scr",
                                          filetypes=[("SCR", "*.scr")]
                                          )
            self.fileName = s
        if self.fileName != '':
            file=open(self.fileName, 'w')
            file.write("{0}\n{1}\n{2}\n".format(self.lettersFile,
                                                self.dictionaryFile,
                                                self.nbLetters
                                                ))
            file.write(self.fileHistory + '\n')
            file.close()
        else:
            self.fileName = None

    def saveAs(self, event = None):
        self.fileName = None
        self.saveGame()

    def _gamesDir(self):
        """Returns the games directory : ../games from here."""
        return os.path.join(PROGDIR, "games")

    def _dictionariesDir(self):
        """Returns the dictionaries directory : ../dictionaries from here."""
        return os.path.join(PROGDIR, "dictionaries")

    def _lettersDir(self):
        return os.path.join(PROGDIR, "letters")

    def preferencesBox(self, event = None):
        box = self.PreferencesBox(self)
        if box != None:
            box.destroy()
        if box.result != None:
            err = False
            if os.path.isfile(box.result[0]):
                p1 = box.result[0]
            else:
                err = True

            if not err and os.path.isfile(box.result[1]):
                p2 = box.result[1]
            else:
                err = True

            try:
                nb = int(box.result[2])
                if nb > 0:
                    p3 = nb
                else:
                    err = True
            except ValueError:
                err = True

            if not err:
                self.setConfig(p1, p2, p3)
                self.saveConfig()
            else:
                self.preferencesBox()

    class PreferencesBox(Dialog):
        def __init__(self, parent):
            self.app = parent
            Dialog.__init__(self, parent, title = "Définir des paramètres par défaut")

            #self.app = parent

        def body(self, master):
            box = tk.Frame(self)
            lbl1 = tk.Label(box, text="Fichier de lettres (*.let) :")
            lbl1.grid(row=0, column=0)
            self.lettersFileEntry = tk.Entry(box, width=30, font=style["msgbox"])
            self.lettersFileEntry.insert(0, self.app.lettersFile)
            self.lettersFileEntry.grid(row=0, column=1)
            b1 = tk.Button(box, text="Parcourir", command=self.lettersOpenFile)
            b1.grid(row=0,column=2)
            #---
            lbl2 = tk.Label(box, text="Fichier de dictionnaire (*.dic) :")
            lbl2.grid(row=1, column=0)
            self.dictionaryFileEntry = tk.Entry(box, width=30, font=style["msgbox"])
            self.dictionaryFileEntry.insert(0, self.app.dictionaryFile)
            self.dictionaryFileEntry.grid(row=1, column=1)
            b2 = tk.Button(box, text="Parcourir", command=self.dictionaryOpenFile)
            b2.grid(row=1,column=2)
            #---
            lbl3 = tk.Label(box, text="Nombre de lettres :")
            lbl3.grid(row=2, column=0)
            self.nbLettersEntry = tk.Entry(box, width=2, font=style["msgbox"])
            self.nbLettersEntry.insert(0, self.app.nbLetters)
            self.nbLettersEntry.grid(row=2, column=1)

            box.pack()
            return self.lettersFileEntry

        def lettersOpenFile(self):
            lettersDir = self.app._lettersDir()
            s = filebox.askopenfilename(title = "Ouvrir un jeu de lettres...", initialdir=lettersDir, filetypes=[("LET", "*.let")])
            print(s)
            print(lettersDir)
            if lettersDir in s:
                s = s.replace(lettersDir, ".." + os.sep + "letters")
            self.lettersFileEntry.delete(0, tk.END)
            self.lettersFileEntry.insert(0, s)

        def dictionaryOpenFile(self):
            dictionaryDir = self.app._dictionariesDir()
            s = filebox.askopenfilename(title = "Ouvrir un dicitonnaire...", initialdir=dictionaryDir, filetypes=[("DIC", "*.dic")])
            print(s)
            print(dictionaryDir)
            if dictionaryDir in s:
                s = s.replace(dictionaryDir, ".." + os.sep + "dictionaries")
            self.dictionaryFileEntry.delete(0, tk.END)
            self.dictionaryFileEntry.insert(0, s)

        def validate(self):
            self.result = (self.lettersFileEntry.get(),
                           self.dictionaryFileEntry.get(),
                           self.nbLettersEntry.get())
            return 1

    def pickLetters2(self, event = None):
        """Automatic picking"""
        self.pickLetters("")

    def pickLetters(self, letters):
        action = self.game.PickLetters(letters)
        if not action[0]:
            msgbox.showerror("Tirage invalide", action[1])
            return False
        else:
            self.canvas.Refresh()
            self.fileHistory += "{0} ".format(action[1])
            return True

    def pickLettersBox(self, event = None):
        box = self.PickLettersBox(self)
        if box != None:
            box.destroy()
        if box.result != None:
            if not self.pickLetters(box.result):
                self.pickLettersBox()

    class PickLettersBox(Dialog):
        def __init__(self, parent):
            Dialog.__init__(self, parent, title = "Piocher des lettres")
            #self.app = parent

        def body(self, master):
            lbl = tk.Label(master, text="Tirage [A..Z?/] :",
                           font=style["msgbox"])
            lbl.grid(row = 0, column = 0)
            self.entry = tk.Entry(master, width=8, font=style["msgbox"])
            self.entry.grid(row=0, column=1)

            return self.entry

        def validate(self):
            self.result = self.entry.get()
            return 1

    def findSolution(self, event = None):
        result = self.game.FindBestWord()
        if len(result)>0:
            self.rightRegion.listOfResults.refresh(result)
        else:
            msgbox.showerror("Aucun coup jouable.")

    def playWord(self, word, position):
        rack = self.game._rack()
        action = self.game.PlaceWord(word, position)
        if action[0] <= 0:
            msgbox.showerror("Mot invalide", action[1])
            return False
        else:
            self.canvas.Refresh()
            self.rightRegion.listOfTurns.addTurn(rack, position, action[0], word)
            self.rightRegion.listOfResults.clean()
            self.fileHistory += ":{0}:{1}\n".format(word, position)

            return True

    def playWordBox(self, event = None):
        box = self.PlayWordBox(self)
        #box.mainloop()

        if box.result != None and box.result[0] != "" and box.result[1]!="":
            if not self.playWord(box.result[0], box.result[1]):
                self.playWordBox()

    class PlayWordBox(Dialog):
        def __init__(self, parent):
            Dialog.__init__(self, parent, title = "Jouer un mot")
            #self.app = parent

            self.word=""
            self.position=""

        def body(self, master):
            lbl = tk.Label(master, text="Mot joué [A..Za..z] :",
                           font=style["msgbox"])
            lbl.grid(row = 0, column = 0)
            lbl2 = tk.Label(master, text="Position [A..Z01..15] :",
                            font=style["msgbox"])
            lbl2.grid(row = 1, column = 0)

            self.wordEntry = tk.Entry(master, width=15, font=style["msgbox"])
            self.wordEntry.grid(row=0, column=1)
            self.positionEntry = tk.Entry(master, width=3, font=style["msgbox"])
            self.positionEntry.grid(row=1, column=1)

            return self.wordEntry

        def validate(self):
            self.result = (self.wordEntry.get(), self.positionEntry.get())
            return 1

    class MenuBar(tk.Menu):
        def __init__(self, master):
            tk.Menu.__init__(self, master)
            self.app = master

            def about():
                msgbox.showinfo("À propos",
                                """Scrabble solver v0 \n réalisé par Xavier MORIN \n-- 01/2018""",
                                icon=msgbox.INFO)

            menu1 = tk.Menu(self, tearoff=0)
            menu1.add_command(label="Nouvelle partie", command=self.app.newGame, accelerator='{}-N'.format(keyOPT))
            menu1.add_command(label="Ouvrir partie", command=self.app.loadGame , accelerator='{}-O'.format(keyOPT))
            menu1.add_separator()
            menu1.add_command(label="Enregistrer", command=self.app.saveGame, accelerator='{}-S'.format(keyOPT))
            menu1.add_command(label="Enregistrer sous...", command=self.app.saveAs , accelerator='{}-Shift-S'.format(keyOPT))
            menu1.add_separator()
            menu1.add_command(label="Options", command=self.app.preferencesBox)
            menu1.add_separator()
            menu1.add_command(label="Quitter", command=self.app.quitApp, accelerator='{}-Q'.format(keyOPT))
            self.add_cascade(label="Fichier", menu=menu1)

            menu2 = tk.Menu(self, tearoff=0)
            menu2.add_command(label="Piocher des lettres", command=self.app.pickLettersBox, accelerator='{}-T'.format(keyOPT))
            menu2.add_command(label="Piochage automatique", command=self.app.pickLetters2, accelerator='{}-Shift-T'.format(keyOPT))
            menu2.add_separator()
            menu2.add_command(label="Trouver le meilleur coup", command=self.app.findSolution, accelerator='{}-F'.format(keyOPT))
            menu2.add_separator()
            menu2.add_command(label="Jouer un mot", command=self.app.playWordBox, accelerator='{}-P'.format(keyOPT))
            self.add_cascade(label="Jeu", menu=menu2)

            menu3 = tk.Menu(self, tearoff=0)
            menu3.add_command(label="Comment jouer", command=about)
            menu3.add_separator()
            menu3.add_command(label="À propos", command=about)
            self.add_cascade(label="Aide", menu=menu3)

    class LeftRegion(tk.Canvas):
        def __init__(self, master):
            self.tileSize = 30
            tk.Canvas.__init__(self, master,
                            width=17*self.tileSize,
                            height=19.5*self.tileSize,
                            borderwidth=0,
                            highlightthickness=0,
                            bg=style["bg"], #006666", #123456",
                            cursor="hand1")

            self.game = None
            self.pack()

        def Refresh(self):
            # deletes every object
            for tag in self.find_all():
                self.delete(tag)

            # draws everything
            self.drawGrid()
            if self.game != None:
                self.drawPlayedLetters()
                self.drawRack()
                self.drawRemainder()

        def drawLetter(self, letter, posX, posY):
            """ draw a letter at posX, posY"""
            # shadow
            x0, y0 = posX+2,posY+2
            x1, y1 = posX+self.tileSize-3, posY+self.tileSize-3
            t = [x0, y0, x0, y1, x1, y1, x1, y0]
            self.create_polygon(t, fill="#342D27", smooth=0, joinstyle=tk.ROUND, width=5, outline ="#342D27")

            # outline
            x0, y0 = posX,posY
            x1, y1 = posX+self.tileSize-4, posY+self.tileSize-4
            t = [x0, y0, x0, y1, x1, y1, x1, y0]
            self.create_polygon(t, fill="#FFFFF0", smooth=0, joinstyle=tk.ROUND, width=5, outline="#808080")
            # face
            self.create_polygon(t,smooth=0, joinstyle=tk.ROUND,
                                fill="#FFFFF0",
                                activefill="#CCCCCC",
                                width=3,
                                outline="#FFFFF0",
                                activeoutline="#CCCCCC")

            if letter.value > 0:
                # Normal letter
                self.create_text(posX+7, posY+4, text = letter.face, fill="#342D27",
                                 font=style["letter"],
                                 anchor=tk.NW)
                self.create_text(posX+self.tileSize-4, posY+self.tileSize-3, text = letter.value, fill="#342D27",
                                 font=style["letter2"],
                                 anchor=tk.SE)
            else:
                # Joker
                self.create_text(posX+13, posY+1, text = letter.face, fill="#FF00FF",
                                 font=style["joker"],
                                 anchor=tk.N)

        def drawRack(self):
            case = self.tileSize
            for i, letter in enumerate(self.game.rack):
                self.drawLetter(letter, (i+5)*case, 17*case)

        def drawPlayedLetters(self):
            case = self.tileSize
            for j, line in enumerate(self.game.grid):
                for i, square in enumerate(line):
                    if square.letter != None:
                        self.drawLetter(square.letter, (i+1)*case, (j+1)*case)

        def drawRemainder(self):
            s = self.game._remainder()
            s2= ""
            while len(s)>51:
                s2 += s[:51] + '\n'
                s = s[51:]
            s2 += s
            self.create_text(10,19.2*self.tileSize, text=s2, fill="#FFFFFF", font=style["remainder"], anchor=tk.SW)

        def drawGrid(self):
            case = self.tileSize
            d=22
            # board
            self.create_rectangle(case-d, case-d, 16*case+d, 16*case+d, fill="#CCCBB3", outline="#342D27", width=3)
            #fill="#BEBD9E"

            # Triple Word, Double Word, Triple Letter, Double Letter squares.
            listTW = "A1,A8,A15,H1,H15,O1,O8,O15".split(',')
            for pos in listTW:
                x, y = self._coord(pos)
                self.create_rectangle(case*x,case*y,case*(x+1),case*(y+1),fill="#E40234")

            listDW = "B2,B14,C3,C13,D4,D12,E5,E11,H8,N2,N14,M3,M13,L4,L12,K5,K11".split(',')
            for pos in listDW:
                x, y = self._coord(pos)
                self.create_rectangle(case*x,case*y,case*(x+1),case*(y+1),fill="#F0878C")

            listTL = "B6,B10,F2,F6,F10,F14,J2,J6,J10,J14,N6,N10".split(',')
            for pos in listTL:
                x, y = self._coord(pos)
                self.create_rectangle(case*x,case*y,case*(x+1),case*(y+1),fill="#4FA1B9")

            listDL = "A4,A12,C7,C9,D1,D8,D15,G3,G7,G9,G13,H4,H12,I3,I7,I9,I13,L1,L8,L15,M7,M9,O4,O12".split(',')
            for pos in listDL:
                x, y = self._coord(pos)
                self.create_rectangle(case*x,case*y,case*(x+1),case*(y+1),fill="#C2DDE8")

            # center polygon
            t=[]
            N= 40
            for i in range(N):
                x = 8.5*case + case*(2+cos(i/N*16*pi))*sin(i/N*2*pi)/7
                y = 8.5*case - case*(2+cos(i/N*16*pi))*cos(i/N*2*pi)/7
                t.append(x)
                t.append(y)

            self.create_polygon(t, fill="#342D27", smooth=1)

            # Grid
            for i in range(16):
                self.create_line(case,case*(i+1),case*(15+1),case*(i+1), fill="#F0F0F0", width=2, cap=tk.PROJECTING)
                self.create_line(case*(i+1),case,case*(i+1),case*(15+1), fill="#F0F0F0", width=2, cap=tk.PROJECTING)

            # coordinates
            for i in range(15):
                self.create_text(case*(3/2+i),case*2/3,text=str(i+1), font=style["coordinates"], fill="#342D27")
                self.create_text(case*2/3, case*(3/2+i),text=chr(65+i), font=style["coordinates"], fill="#342D27")

        def _coord(self, pos):
            """Returns  (i,j) from *Ann* string"""
            i = ord(pos[0])-64
            j = eval(pos[1:])
            return (i,j)

        class Tile(ScrabbleGame.Letter):
            """Class representing a letter in the canvas"""
            def __init__(self):
                pass

    class RightRegion(tk.PanedWindow):
        def __init__(self, master):
            tk.PanedWindow.__init__(self, master, orient=tk.VERTICAL, bd=0, bg=style["bg"], sashwidth=0)
            self.app = master.master
            self.pack()
            self.listOfTurns = self.ListOfTurns(self)
            self.add(self.listOfTurns)
            self.add(tk.Button(self, text="Piocher des lettres", font=style["msgbox"], highlightbackground=style["bg"], command=self.app.pickLettersBox))
            self.add(tk.Button(self, text="Trouver solution", font=style["msgbox"], highlightbackground=style["bg"], command=self.app.findSolution))
            self.add(tk.Button(self, text="Jouer un mot", font=style["msgbox"], highlightbackground=style["bg"], command=self.app.playWordBox))
            self.listOfResults = self.ListOfResults(self)
            self.add(self.listOfResults)
            self.add(tk.Canvas(height=7, bg=style["bg"], bd=0, borderwidth=0, highlightthickness=0))
        def clean(self):
            self.listOfTurns.clean()
            self.listOfResults.clean()

        class ListOfTurns(tk.Listbox):
            def __init__(self, master):
                self.app = master.app
                master.add(tk.Label(master, text="n°:  Tirage  : Pos : Pts : Tot : Mots joués", bg=style["bg"], font=style["msgbox"], anchor=tk.W, padx=-1))
                tk.Listbox.__init__(self, master, font=style["msgbox"], width=50, height=15, bg="#CDD8D8")

                self.totalScore = 0
                self.turnsCount = 0
                self.pack()

            def addTurn(self, rack, position, score, word):
                self.totalScore += score
                self.turnsCount += 1
                self.insert(tk.END, "{0:>2}: {1:<9}: {2:<3} :{3:>4} :{4:>4} : {5}".format(self.turnsCount,
                                                                                rack,
                                                                                position,
                                                                                score,
                                                                                self.totalScore,
                                                                                word))
                self.see(tk.END)

            def clean(self):
                self.totalScore = 0
                self.turnsCount = 0
                self.delete(0, tk.END)

        class ListOfResults(tk.Listbox):
            def __init__(self, master):
                master.add(tk.Label(master, text="Pts : Pos : Mots jouables", font=style["msgbox"], bg=style["bg"], anchor=tk.W, padx=-1))
                tk.Listbox.__init__(self, master, font=style["msgbox"], width=50, height=13, bg="#CDD8D8")

                self.app = master.app
                self.bind("<Double-Button-1>", self.dbleClick1)
                self.pack()

            def refresh(self, listOfWords):
                self.delete(0, tk.END)
                for l in listOfWords:
                    #l format : [word, position, score]
                    self.insert(tk.END, "{0:>4}: {1:<3} : {2}".format(l[2],l[1],l[0]))

            def clean(self):
                self.delete(0, tk.END)

            def dbleClick1(self, event):
                # repérer la ligne
                # traiter la ligne
                if len(self.curselection()) == 1:
                    s = self.get(self.curselection())
                    s = s.replace(' ', '')
                    t = s.split(':')
                    self.app.playWord(t[2],t[1])


import time

from osv import osv
from osv import fields
from tools import config
from tools.translate import _
from datetime import datetime
from datetime import timedelta

class hr_payroll_declar(osv.osv):
    '''
    Decleration Form
    '''
    _name = 'hr.payroll.declare'
    _description = 'Decleration Form'

    _columns = {
        'name':fields.char('Name', size=1024, required=False),
        'company_id':fields.many2one('res.company', 'Company', required=True),
        'employee_id':fields.many2one('hr.employee', 'Employee', required=True),
        'income_sal': fields.float('Income by Salary', digits=(16, int(config['price_accuracy'])), readonly=True),
        'income_ids':fields.one2many('hr.payroll.declare.line', 'income_id', 'Source Of Income', required=False),
        'investment_ids':fields.one2many('hr.payroll.declare.line', 'invest_id', 'Investments', required=False),
        'claim_ids':fields.one2many('hr.payroll.declare.line', 'claim_id', 'Allowance to Claime', required=False),
        'date': fields.date('Date'),

        'income': fields.float('Taxable Income', digits=(16, int(config['price_accuracy'])), readonly=True),
        'investment': fields.float('Total Investment', digits=(16, int(config['price_accuracy'])), readonly=True),
        'claims': fields.float('Total Allowance Claims', digits=(16, int(config['price_accuracy'])), readonly=True),

        'state':fields.selection([
            ('draft','Draft'),
            ('pending','Waiting for Review'),
            ('pending','Approved by HR'),
            ('done','Confirm'),
        ],'State', select=True, readonly=True),
        'note': fields.text('Description'),
    }

    def get_basic(self, cr, uid, ids, context):
        res = {}
        for rs in self.browse(cr, uid, ids, context):
            period_id = self.pool.get('account.period').search(cr,uid,[('date_start','<=',time.strftime('%Y-%m-%d')),('date_stop','>=',time.strftime('%Y-%m-%d'))])[0]
            fiscalyear_id = self.pool.get('account.period').browse(cr, uid, period_id).fiscalyear_id
            sql_req= '''
                SELECT c.id as id, c.wage as wage, function as function, c.date_start as start, c.date_end as end
                FROM hr_contract c
                  LEFT JOIN hr_employee emp on (c.employee_id=emp.id)
                  LEFT JOIN hr_contract_wage_type cwt on (cwt.id = c.wage_type_id)
                  LEFT JOIN hr_contract_wage_type_period p on (cwt.period_id = p.id)
                WHERE
                  (emp.id=%s) AND
                  (date_start >= %s) AND
                  (date_end IS NULL OR date_end <= %s)
                '''
            cr.execute(sql_req, (rs.employee_id.id, fiscalyear_id.date_start, fiscalyear_id.date_stop))
            contracts = cr.dictfetchall()
            if not contracts:
                raise osv.except_osv(_('Contract Error !'), _('No Contract Defined for : %s ' % (rs.employee_id.name)))
            total = 0.0

            line_ids = []
            for lines in rs.claim_ids:
                line_ids += [lines.head_id.id]

            for ct in contracts:
                allow = 0.0

                d1 = ct['start']
                d2 = ct['end'] or fiscalyear_id.date_stop
                td = datetime.fromtimestamp(time.mktime(time.strptime(d2, '%Y-%m-%d'))) - datetime.fromtimestamp(time.mktime(time.strptime(d1, '%Y-%m-%d')))
                total += (td.days / 30) * ct['wage']

#                ct = self.pool.get('hr.contract').browse(cr, uid, ct['id'])
#                for line in ct.function.line_ids:
#                    if line.category_id.id in line_ids:
#                        if line.amount_type == 'fix':
#                            allow += (td.days / 30) * line.amount
#                        elif line.amount_type == 'per':
#                            allow += (total * line.amount)
#                        print 'XXXXXXXXXXXXXXXXXXXXXXX : ', line.name, allow

            res[rs.id] = total
        return res

    def write(self, cr, user, ids, vals, context=None):
        res = self.get_basic(cr, user, ids, context)
        for id in ids:
            vals['income_sal'] = res[id]
            super(hr_payroll_declar, self).write(cr, user, [id], vals, context)

        return res

hr_payroll_declar()

class hr_payroll_declare_line(osv.osv):
    '''
    Decleration Line
    '''
    _name = 'hr.payroll.declare.line'
    _description = 'Decleration Line'

    def _function_call(self, cr, uid, ids, field_names, arg, context={}):
        res = {}
        for rs in self.browse(cr, uid, ids, context):
            val = 0.0

            if rs.income_id:
                pass
            elif rs.invest_id:
                pass
            elif rs.claim_id:
                if rs.head_id.calc_type == 'min_max':
                    if rs.amount < rs.head_id.min:
                        val = rs.head_id.min
                    elif rs.amount >= rs.head_id.min and rs.amount <= rs.head_id.max:
                        val = rs.amount
                    elif rs.amount > rs.head_id.max:
                        val = rs.head_id.max

            res[rs.id] = val

        return res

    _columns = {
        'name':fields.char('Name', size=64, required=False),
        'note': fields.text('Description'),
        'income_id':fields.many2one('hr.payroll.declare', 'Income', required=False),
        'invest_id':fields.many2one('hr.payroll.declare', 'Investment', required=False),
        'claim_id':fields.many2one('hr.payroll.declare', 'Allowance Claims', required=False),
        'amount': fields.float('Amount', digits=(16, int(config['price_accuracy']))),
        'allow': fields.float('Allowence', digits=(16, int(config['price_accuracy']))),
        'allow_amount': fields.function(_function_call, method=True, type='float', digits=(16, int(config['price_accuracy'])), string='Allow Amount'),
        'head_id':fields.many2one('hr.allounce.deduction.categoty', 'Allowance / Deduction', required=True),

    }
hr_payroll_declare_line()

class payment_category(osv.osv):
    '''
    Allowance Deduction Categoty
    '''
    _inherit = 'hr.allounce.deduction.categoty'

    _columns = {
        'calc_type':fields.selection([
            ('min_max','Min / Max'),
            ('stmt','List of Calculations'),
            ('range','Selection from Range'),
        ],'Calculation Type', select=True, readonly=False),
        'min': fields.float('Min Value', digits=(16, int(config['price_accuracy']))),
        'max': fields.float('Max Value', digits=(16, int(config['price_accuracy']))),

        'stmt_ids':fields.one2many('hr.payroll.declare.stmt', 'category_id', 'Functions', required=False),
        'stmt_select':fields.selection([
            ('min','Minimum'),
            ('max','Maximum'),
            ('avg','Average'),
        ],'Selection Method', select=True, readonly=False),
    }
    _defaults = {
        'stmt_select': lambda *a: 'min',
        'calc_type': lambda *a: 'min_max'
    }
payment_category()

class payment_stmt(osv.osv):
    '''
    Open ERP Model
    '''
    _name = 'hr.payroll.declare.stmt'
    _description = 'Payroll Calculations'

    _columns = {
        'category_id':fields.many2one('hr.allounce.deduction.categoty', 'Category', required=True),
        'name':fields.char('Expression', size=1024, required=True, readonly=False),
        'sequence': fields.integer('Sequence'),
        'active':fields.boolean('Active', required=False),
    }
    _defaults = {
        'sequence': lambda *a: 5,
        'active': lambda *a: True
    }
payment_stmt()


# ----------- Import statements ------------

import math;
import numpy;
import matplotlib.pyplot as plt;

# ------------ Custom functions ------------

# A-F sums
def A(xlist, ylist, y_uncert):
    A = 0;
    for i in range(len(xlist)):
        A += xlist[i] / (y_uncert[i])**2;
    return A;

def B(xlist, ylist, y_uncert):
    B = 0;
    for i in range(len(xlist)):
        B += 1.0 / (y_uncert[i])**2;
    return B;

def C(xlist, ylist, y_uncert):
    C = 0;
    for i in range(len(xlist)):
        C += ylist[i] / (y_uncert[i])**2;
    return C;

def D(xlist, ylist, y_uncert):
    D = 0;
    for i in range(len(xlist)):
        D += (xlist[i])**2 / (y_uncert[i])**2;
    return D;

def E(xlist, ylist, y_uncert):
    E = 0;
    for i in range(len(xlist)):
        E += (xlist[i]) * (ylist[i]) / (y_uncert[i])**2;
    return E;

def F(xlist, ylist, y_uncert):
    F = 0;
    for i in range(len(xlist)):
        F += (ylist[i])**2 / (y_uncert[i])**2;
    return F;

# chi-square
def s_m(xlist, ylist, y_uncert, a, b):
    s_m = 0;
    for i in range(len(xlist)):
        s_m += (ylist[i] - a*xlist[i] - b)**2 / (y_uncert[i])**2;
    return s_m;

# average y-value
def avg(alist):
    avg = 0;
    for i in range(len(alist)):
        avg += ylist[i];
    return avg;

# coefficient of determination (r^2)
def r2(xlist, ylist, y_uncert, a, b, y_avg):
    r2 = 0;
    num = 0;
    denom = 0;
    for i in range(len(xlist)):
        num += (a*xlist[i] + b - y_avg)**2;
        denom += (ylist[i] - y_avg)**2;
    r2 = num / denom;
    return r2;

# ------------ Hardcode section ------------

# Hardcode these values
xlist = [65, 75, 85, 95, 105];
x_uncert = [];

ylist = [-20, 17, 42, 94, 127];
y_uncert = [1, 1, 1, 1, 1];

# -------------- Main program --------------

# Calculate average y-value
y_avg = avg(ylist);

# Assign the A-F sum values to variables A-F
A = A(xlist, ylist, y_uncert);
B = B(xlist, ylist, y_uncert);
C = C(xlist, ylist, y_uncert);
D = D(xlist, ylist, y_uncert);
E = E(xlist, ylist, y_uncert);
F = F(xlist, ylist, y_uncert);

# y = ax + b is the best-fit line
a = (B*E - A*C) / (B*D - A*A)
b = (C*D - A*E) / (B*D - A*A)

# Calculate chi-square
s_m = s_m(xlist, ylist, y_uncert, a, b);
# Calculate degrees of freedom
ndf = len(xlist) - 2;        # 2 parameters: a,b
# Calculate closeness of fit (should be as close to 1 as possible)
fit = s_m / ndf;

# Calculate coefficient of determination r^2 and r
r2 = r2(xlist, ylist, y_uncert, a, b, y_avg);
if (a > 0):
    r = math.sqrt(r2);
else:
    r = -math.sqrt(r2);

# ------------ Console output  -------------

# Print the linear regression model
print('T = ' + str(b) + ' + ' + str(a) +'P');       # equation y=ax+b
print("A = " + str(b));                             # a-value
print("B = " + str(a));                             # b-value
print("S_m = " + str(s_m) + " (chi-square)");       # chi-square
print("ndf = " + str(ndf));                         # degrees of freedom
print("S_m/ndf = " + str(fit));                     # closeness of fit (chi-square)
print("p-value ~ 0 due to large S_m");              # p-value
print("coeff. of determ.: r^2 = " + str(r2));       # coefficient of determination
print("correlation coeff.: r = " + str(r));         # correlation coefficient
print("\n");
print("Absolute zero (accepted): -273.15 C");       # theoretical value of absolute zero
print("Absolute zero (fitted): " + str(b) + " C");  # experimental value of absolute zero

# ------------- File output  ---------------

f = open("shi_homework05_results.txt", "w+")

# Print the linear regression model
f.write('T = ' + str(b) + ' + ' + str(a) +'P' + "\n");       # equation y=ax+b
f.write("A = " + str(b) + "\n");                             # a-value
f.write("B = " + str(a) + "\n");                             # b-value
f.write("S_m = " + str(s_m) + " (chi-square)" + "\n");       # chi-square
f.write("ndf = " + str(ndf) + "\n");                         # degrees of freedom
f.write("S_m/ndf = " + str(fit) + "\n");                     # closeness of fit (chi-square)
f.write("p-value ~ 0 due to large S_m" + "\n");              # p-value
f.write("coeff. of determ.: r^2 = " + str(r2) + "\n");       # coefficient of determination
f.write("correlation coeff.: r = " + str(r) + "\n");         # correlation coefficient
f.write("\n");
f.write("Absolute zero (accepted): -273.15 C" + "\n");       # theoretical value of absolute zero
f.write("Absolute zero (fitted): " + str(b) + " C" + "\n");  # experimental value of absolute zero

f.close();

# ------------ Plotting output -------------

# Set parameters for plot
print("range of x-values: [" + str(min(xlist)) + " , " + str(max(xlist)) + "]");
print("range of y-values: [" + str(min(ylist)) + " , " + str(max(ylist)) + "]");

print("Enter min and max for axes of plot:");
xmin = float(input("xmin: "));
xmax = float(input("xmax: "));
ymin = float(input("ymin: "));
ymax = float(input("ymax: "));

equation = 'y=' + str(a) + 'x+' + str(b);

# Plot the axes and labels (need to hardcode xlabel and ylabel)
plt.title("Temperature vs Pressure");
plt.xlabel("P (mm Hg)");
plt.ylabel("T (degrees C)");
plt.axis([xmin, xmax, ymin, ymax]);
# Plot the data points
plt.plot(xlist, ylist, 'ro');
# Plot the best-fit line
x = numpy.linspace(xmin,xmax,100);
y = a*x+b;
plt.plot(x, y, '-r', label=equation);
plt.legend(loc='upper left');

plt.show();


import logging
import multiprocessing
import os
from subprocess import run

from Bio import SeqIO, AlignIO
from Bio.Seq import Seq
from Bio.SeqRecord import SeqRecord

from constants import CD_HIT_CLUSTER_REPS_OUTPUT_FILE, CLUSTERS_NT_SEQS_DIR, CLUSTERS_ALIGNMENTS_DIR, \
    NUMBER_OF_PROCESSES, FASTA_FILE_TYPE, ALIGNMENTS_FOR_TREE_DIR, DATA_DIR, ALIGNMENT_STRAIN_PATTERN, STRAINS_COUNT
from data_analysis import build_strain_names_map
from logging_config import worker_configurer

def perform_clustering_on_proteins(aggregated_proteins_file_path):
    """Run the CD-HIT program to perform clustering on the strains"""
    logger = logging.getLogger()
    logger.info("Running CD-HIT on combined proteins file to create clustering")
    cd_hit_args = " ".join(["cd-hit", "-i", aggregated_proteins_file_path, "-o", CD_HIT_CLUSTER_REPS_OUTPUT_FILE, "-c 0.70",
                   "-n 5", "-M 16000", "-g 1", "-p 1"])
    cd_hit_return_code = run(cd_hit_args, shell=True).returncode
    logger.info("Finished running CD-HIT with return code %d" % cd_hit_return_code)
    return cd_hit_return_code

def perform_clustering_on_cds(input_file, output_file):
    """Run the CD-HIT-EST program to perform clustering on the strains representatives and pseudogenes"""
    logger = logging.getLogger()
    logger.info("Running CD-HIT-EST on combined representative and pseudogene cds file to create clustering")
    cd_hit_est_args = " ".join(["cd-hit-est", "-i", input_file, "-o", output_file, "-c 0.8",
                   "-n 5", "-M 16000", "-g 1", "-p 1", "-d 30"])
    cd_hit_est_return_code = run(cd_hit_est_args, shell=True).returncode
    logger.info("Finished running CD-HIT with return code %d" % cd_hit_est_return_code)
    return cd_hit_est_return_code

def perform_alignment_on_core_clusters(log_queue):
    """Run MAFFT & Gblocks tools on fasta files of protein nucleotide seqs for each core cluster"""
    logger = logging.getLogger(__name__)
    logger.info("Running MAFFT & Gblocks on core clusters for alignment")
    if not os.path.exists(CLUSTERS_NT_SEQS_DIR):
        logger.error("No clusters dir found, exiting")
        exit(1)
    if not os.path.exists(CLUSTERS_ALIGNMENTS_DIR):
        os.makedirs(CLUSTERS_ALIGNMENTS_DIR)

    job_queue = multiprocessing.Queue()
    prepare_alignment_jobs(job_queue)
    workers = [
        multiprocessing.Process(target=perform_alignment_and_pruning, args=(i, job_queue, worker_configurer, log_queue))
        for i in range(NUMBER_OF_PROCESSES)]
    for w in workers:
        w.start()
    job_queue.put(None)
    for w in workers:
        w.join()
    logger.info("Finished running MAFFT for all clusters")

def prepare_alignment_jobs(job_queue):
    """Put all downloaded strain dirs in job queue for workers"""
    core_clusters = os.listdir(CLUSTERS_NT_SEQS_DIR)
    for cluster_file in core_clusters:
        job_queue.put(cluster_file)

def perform_alignment_and_pruning(worker_id, job_queue, configurer, log_queue):
    """
    Perform MAFFT alignment and Gblocks pruning for a core cluster fasta file
    """
    configurer(log_queue)
    logger = logging.getLogger(__name__ + "_worker_" + str(worker_id))
    while True:
        cluster_file = job_queue.get()
        if cluster_file is None:
            job_queue.put(None)
            break
        logger.info("Running MAFFT for %s" % cluster_file)
        alignment_stdout = open("alignment_stdout.log", "w")
        alignment_stderr = open("alignment_stderr.log", "w")
        cluster_alignment_filename = cluster_file + "_alignment"
        if not os.path.exists(os.path.join(CLUSTERS_ALIGNMENTS_DIR, cluster_alignment_filename)):
            cluster_alignment_file = open(os.path.join(CLUSTERS_ALIGNMENTS_DIR, cluster_alignment_filename), 'w')
            mafft_args = " ".join(["mafft", "--auto", os.path.join(CLUSTERS_NT_SEQS_DIR, cluster_file)])
            mafft_return_code = run(mafft_args, shell=True, stdout=cluster_alignment_file, stderr=alignment_stderr).returncode
            logger.info("Finished running MAFFT for %s with return code %d" % (cluster_file, mafft_return_code))
            cluster_alignment_file.close()

        logger.info("Running GBlocks for %s" % cluster_file)
        gblocks_args = " ".join(["Gblocks", os.path.join(CLUSTERS_ALIGNMENTS_DIR, cluster_alignment_filename), "-t=d", "-b5=a", "-p=n"])
        gblocks_return_code = run(gblocks_args, shell=True, stdout=alignment_stdout, stderr=alignment_stderr).returncode
        logger.info(
            "Finished running Gblocks for alignment %s with return code %d" % (cluster_alignment_filename, gblocks_return_code))

def prepare_alignments_for_tree(log_queue):
    """Edit each alignment to remove invariant positions, pad missing strain seqs & concatenate all alignments"""
    logger = logging.getLogger(__name__)
    logger.info("Preparing core clusters alignments for tree")
    if not os.path.exists(CLUSTERS_ALIGNMENTS_DIR):
        logger.error("No alignments dir found, exiting")
        exit(1)
    if not os.path.exists(ALIGNMENTS_FOR_TREE_DIR):
        os.makedirs(ALIGNMENTS_FOR_TREE_DIR)

    job_queue = multiprocessing.Queue()
    prepare_alignment_editing_jobs(job_queue)
    workers = [
        multiprocessing.Process(target=perform_alignment_editing, args=(i, job_queue, worker_configurer, log_queue))
        for i in range(NUMBER_OF_PROCESSES)]
    for w in workers:
        w.start()
    job_queue.put(None)
    for w in workers:
        w.join()
    logger.info("Finished editing all alignments, concatenating")
    edited_alignment_files = os.listdir(ALIGNMENTS_FOR_TREE_DIR)
    concatenated_alignment = None
    concatenated_alignment_file = os.path.join(DATA_DIR, "all_alignments")
    for edited_alignment_file in edited_alignment_files:
        logger.info("Concatenating alignment %s" % edited_alignment_file)
        with open(os.path.join(ALIGNMENTS_FOR_TREE_DIR, edited_alignment_file), "r") as f:
            edited_alignment = AlignIO.read(f, FASTA_FILE_TYPE)
            if not concatenated_alignment:
                concatenated_alignment = edited_alignment[:, :]
            else:
                concatenated_alignment += edited_alignment[:, :]
    AlignIO.write(concatenated_alignment, open(concatenated_alignment_file, "w"), FASTA_FILE_TYPE)
    logger.info("Finished concatenating all alignments, written to %s" % concatenated_alignment_file)

def prepare_alignment_editing_jobs(job_queue):
    """Put all downloaded strain dirs in job queue for workers"""
    alignments = os.listdir(CLUSTERS_ALIGNMENTS_DIR)
    for alignment_file in alignments:
        if alignment_file.endswith("-gb"):
            job_queue.put(alignment_file)

def perform_alignment_editing(worker_id, job_queue, configurer, log_queue):
    """
    Perform alignment editing
    """
    configurer(log_queue)
    logger = logging.getLogger(__name__ + "_worker_" + str(worker_id))
    while True:
        alignment_file = job_queue.get()
        if alignment_file is None:
            job_queue.put(None)
            break
        logger.info("Editing alignment %s" % alignment_file)
        alignment = AlignIO.read(open(os.path.join(CLUSTERS_ALIGNMENTS_DIR, alignment_file), "r"), FASTA_FILE_TYPE)
        edited_alignment = None
        for col_idx in range(alignment.get_alignment_length()):
            col = alignment[:, col_idx:col_idx + 1]
            col_str = alignment[:, col_idx]
            if not all(c == col_str[0] for c in col_str):
                if not edited_alignment:
                    edited_alignment = col
                else:
                    edited_alignment += col
        alignment_seq_len = edited_alignment.get_alignment_length()
        logger.info("alignment_seq_len = %d" % alignment_seq_len)
        strain_idx = 0
        while strain_idx < STRAINS_COUNT:
            logger.info("in while - strain_idx = %d" % strain_idx)
            if len(edited_alignment) > strain_idx:
                seq = edited_alignment[strain_idx]
                seq_strain_idx = int(ALIGNMENT_STRAIN_PATTERN.match(seq.id).group(1))
                logger.info("checking if strain idx %d < seq_strain_idx %d" % (strain_idx, seq_strain_idx))
                if strain_idx < seq_strain_idx:
                    for i in range(seq_strain_idx - strain_idx):
                        logger.info("adding padded seq at idx %d" % (strain_idx + i))
                        edited_alignment._records.insert(strain_idx + i, SeqRecord(Seq(alignment_seq_len * '-'), id="[%d] padding" % (strain_idx + i)))
                    strain_idx += (seq_strain_idx - strain_idx + 1)
                    continue
                strain_idx += 1
            else:
                logger.info("adding padded seq at end of alignment list")
                edited_alignment.append(SeqRecord(Seq(alignment_seq_len * '-'), id="[%d] padding" % strain_idx))
                strain_idx += 1
        alignment_file_edited = os.path.join(ALIGNMENTS_FOR_TREE_DIR, alignment_file)
        logger.info("Finished padding alignment - writing to file %s" % alignment_file_edited)
        AlignIO.write(edited_alignment, open(alignment_file_edited, "w"), FASTA_FILE_TYPE)

def format_concatenated_alignment():
    logger = logging.getLogger(__name__)
    strain_names_map = build_strain_names_map()
    tree_alignment = AlignIO.read(open(os.path.join(DATA_DIR, "all_alignments"), "r"), FASTA_FILE_TYPE)
    tree_alignment_filtered = AlignIO.MultipleSeqAlignment([])
    for id, strain in zip(range(STRAINS_COUNT), tree_alignment):
        if all(c == '-' for c in strain.seq):
            logger.info("skipping filtered strain %d" % id)
        else:
            logger.info("adding id to strain %d" % id)
            strain.id = "[" + str(id) + "]" + strain_names_map[id]
            strain.description = ''
            tree_alignment_filtered.append(strain)
    AlignIO.write(tree_alignment_filtered, open(os.path.join(DATA_DIR, "filtered_tree_alignment"), "w"), FASTA_FILE_TYPE)


address_book = {}

def add_details(name, phone_no, email):
    contact = {}
    contact["Phone no"] = phone_no
    contact["email"] = email
    address_book[name] = contact
    print(address_book)

def update_detail(args):

    print(args)
    name = args[0]
    phone = args[1]
    email = args[2]

    if name in address_book:
        if phone != "":
            address_book[name]["Phone no"] = phone
        if email != "":
            address_book[name]["email"] = email
    else:
        print(f"{name} is not in address book.")
    return f"{name}: {address_book[name]}"

def delete_data(name):
    if input("are you sure, requested data cannot be recovered once deleted(y or n): ") == "y":
        del address_book[name]
        print("requested data has been deleted.")
    else:
        print("request canceled.")

def search(args):
    name = args[0]
    phone = args[1]
    email = args[2]
    if name != "" and name in address_book:
        return f"{name}: {address_book[name]}"
    elif phone != "" or email != "":
        for data in address_book:
            if address_book[data]["Phone no"] == phone or address_book[data]["email"] == email:
                return f"{data}: {address_book[data]}"
    else:
        print("no data found.")

def request_details():
    name = input("Enter Name: ")
    phone_no = input("Enter phone no.: +91 ")
    email = input("Enter Email: ")
    return name, phone_no, email

Run = True
while Run:
    User_request = input("""
    Please select the service:
    1. add contact
    2. update contact (name required, leave blank requested data if no change required)
    3. delete contact (required name)
    4. search details
    Enter the number:
    """)

    if User_request == "1":
        add_details(*request_details())

    elif User_request == "2":
        print(update_detail(request_details()))

    elif User_request == "3":
        delete_data(request_details()[0])

    elif User_request == "4":
        print(search(request_details()))


import numpy as np

__all__ = ['JustOnceClass', 'just_once', 'Cache']

class JustOnceClass(object):
    '''Base class for classes with methods that should never be executed twice.

       In typically applications, these methods get called many times, but
       only during the first call, an actual computation is carried out. This
       way, the caller can safely call a method, just to make sure that a
       required result is computed.

       All methods in the subclasses that should have this feature, must be
       given the ``just_once`` decoratore, e.g. ::

           class Example(JustOnceClass):
               @just_once
               def do_something():
                   self.foo = self.bar

       When all results are outdated, one can call the ``clear`` method
       to forget which methods were called already.
    '''
    def __init__(self):
        self._done_just_once = set([])

    def __clear__(self):
        self.clear()

    def clear(self):
        self._done_just_once = set([])

def just_once(fn):
    def wrapper(instance):
        if not hasattr(instance, '_done_just_once'):
            raise TypeError('Missing hidden _done_just_once. Forgot to call JustOnceClass.__init__()?')
        if fn.__name__ in instance._done_just_once:
            return
        fn(instance)
        instance._done_just_once.add(fn.__name__)
    wrapper.__doc__ = fn.__doc__
    return wrapper

def _normalize_alloc(alloc):
    '''Normalize the alloc argument of the from_alloc and check_alloc methods'''
    if not hasattr(alloc, '__len__'):
        alloc = (alloc,)
    if len(alloc) == 0:
        raise TypeError('Alloc can not be an empty list')
    return alloc

def _normalize_tags(tags):
    '''Normalize the tags argument of the CacheItem constructor'''
    if tags is None:
        return set([])
    else:
        return set(tags)

class CacheItem(object):
    '''A container for an object stored in a Cache instance'''
    def __init__(self, value, tags=None):
        '''
           **Arguments:**

           value
                The object stored in this container

           **Optional arguments:**

           tags
                Tags to be associated with the object
        '''
        self._value = value
        self._valid = True
        self._tags = _normalize_tags(tags)

    @classmethod
    def from_alloc(cls, alloc, tags):
        alloc = _normalize_alloc(alloc)
        # initialize a floating point array
        array = np.zeros(alloc, float)
        return cls(array, tags=tags)

    def check_alloc(self, alloc):
        alloc = _normalize_alloc(alloc)
        # check if the array has the correct shape and dtype
        if not (isinstance(self._value, np.ndarray) and
                self._value.shape == tuple(alloc) and
                issubclass(self._value.dtype.type, float)):
            raise TypeError('The stored item does not match the given alloc.')

    def check_tags(self, tags):
        tags = _normalize_tags(tags)
        if tags != self._tags:
            raise ValueError('Tags do not match.')

    def _get_value(self):
        if not self._valid:
            raise ValueError('This cached item is not valid.')
        return self._value

    value = property(_get_value)

    def _get_valid(self):
        return self._valid

    valid = property(_get_valid)

    def _get_tags(self):
        return self._tags

    tags = property(_get_tags)

    def clear(self):
        '''Mark the item as invalid and clear the contents of the object.

           **Returns:** A boolean indicating that clearing was successful
        '''
        self._valid = False
        if isinstance(self._value, np.ndarray):
            self._value[:] = 0.0
        elif hasattr(self._value, '__clear__') and callable(self._value.__clear__):
            self._value.__clear__()
        else:
            return False
        return True

class NoDefault(object):
    pass

no_default = NoDefault()

def _normalize_key(key):
    '''Normalize the key argument(s) of the load and dump methods'''
    if hasattr(key, '__len__') and  len(key) == 0:
        raise TypeError('At least one argument needed to specify a key.')
    # upack the key if needed
    while len(key) == 1 and isinstance(key, tuple):
        key = key[0]
    return key

class Cache(object):
    '''Object that stores previously computed results.

       The cache behaves like a dictionary with some extra features that can be
       used to avoid recomputation or reallocation.
    '''
    def __init__(self):
        self._store = {}

    def clear(self, **kwargs):
        '''Clear all items in the cache

           **Optional arguments:**

           dealloc
                When set to True, the items are really removed from memory.

           tags
                Limit the items cleared to those who have at least one tag
                that matches one of the given tags. When this argument is used
                and it contains at least one tag, items with no tags are not
                cleared.
        '''
        # Parse kwargs. This forces the caller to use keywords in order to avoid
        # confusion.
        dealloc = kwargs.pop('dealloc', False)
        tags = kwargs.pop('tags', None)
        if len(kwargs) > 0:
            raise TypeError('Unexpected arguments: %s' % list(kwargs.keys()))
        # actual work
        tags = _normalize_tags(tags)
        for key, item in list(self._store.items()):
            if len(tags) == 0 or len(item.tags & tags) > 0:
                self.clear_item(key, dealloc=dealloc)

    def clear_item(self, *key, **kwargs):
        '''Clear a selected item from the cache

           **Optional arguments:**

           dealloc
                When set to True, the item is really removed from memory.
        '''
        key = _normalize_key(key)
        dealloc = kwargs.pop('dealloc', False)
        if len(kwargs) > 0:
            raise TypeError('Unexpected arguments: %s' % list(kwargs.keys()))
        item = self._store.get(key)
        if item is None:
            return
        cleared = False
        if not dealloc:
            cleared = item.clear()
        if not cleared:
            del self._store[key]

    def load(self, *key, **kwargs):
        '''Get a value from the cache

           **Arguments:**

           key0 [key1 ...]
                All positional arguments are used as keys to identify the cached
                value.

           **Optional arguments:**

           alloc
                Parameters used to allocate a cached value if it is not present
                yet. This argument can take two forms. When integer or a
                tuple of integers is given, an array is allocated.
                Alternatively, a tuple may be given whose first element is a
                constructor, and further elements are arguments for that
                constructor.

           default
                A default value that is returned when the key does not exist in
                the cache. This default value is not stored in the cache.

           tags
                When alloc is used and a new object is thereby created or
                reused, it will get these tags. This argument is only allowed if
                the alloc argument is present. In case no new object is
                allocated, the given tags must match those already present.

           The optional argument alloc and default are both meant to handle
           situations when the key has not associated value. Hence they can not
           be both present.
        '''
        key = _normalize_key(key)

        # parse kwargs
        alloc = kwargs.pop('alloc', None)
        default = kwargs.pop('default', no_default)
        tags = kwargs.pop('tags', None)
        if not (alloc is None or default is no_default):
            raise TypeError('The optional arguments alloc and default can not be used at the same time.')
        if tags is not None and alloc is None:
            raise TypeError('The tags argument is only allowed when the alloc argument is present.')
        if len(kwargs) > 0:
            raise TypeError('Unknown optional arguments: %s' % list(kwargs.keys()))

        # get the item from the store and decide what to do
        item = self._store.get(key)
        # there are three behaviors, depending on the keyword argumentsL
        if alloc is not None:
            # alloc is given. hence two return values: value, new
            if item is None:
                # allocate a new item and store it
                item = CacheItem.from_alloc(alloc, tags)
                self._store[key] = item
                return item.value, True
            elif not item.valid:
                try:
                    # try to reuse the same memroy
                    item.check_alloc(alloc)
                    item._valid = True # as if it is newly allocated
                    item.check_tags(tags)
                except TypeError:
                    # if reuse fails, reallocate
                    item = CacheItem.from_alloc(alloc, tags)
                    self._store[key] = item
                return item.value, True
            else:
                item.check_alloc(alloc)
                item.check_tags(tags)
                return item.value, False
        elif default is not no_default:
            # a default value is given, it is not stored
            if item is None or not item.valid:
                return default
            else:
                return item.value
        else:
            # no optional arguments are given
            if item is None or not item.valid:
                raise KeyError(key)
            else:
                return item.value

    def __contains__(self, key):
        key = _normalize_key(key)
        item = self._store.get(key)
        if item is None:
            return False
        else:
            return item.valid

    def dump(self, *args, **kwargs):
        '''Store an object in the cache.

           **Arguments:**

           key1 [, key2, ...]
                The positional arguments (except for the last) are used as a key
                for the object.

           value
                The object to be stored.

           **Optional argument:**

           tags
                Tags to be associated with the object
        '''
        tags = kwargs.pop('tags', None)
        if len(kwargs) > 0:
            raise TypeError('Unknown optional arguments: %s' % list(kwargs.keys()))
        if len(args) < 2:
            raise TypeError('At least two arguments are required: key1 and value.')
        key = _normalize_key(args[:-1])
        value = args[-1]
        item = CacheItem(value, tags)
        self._store[key] = item

    def __len__(self):
        return sum(item.valid for item in self._store.values())

    def __getitem__(self, key):
        return self.load(key)

    def __setitem__(self, key, value):
        return self.dump(key, value)

    def __iter__(self):
        return iter(self.keys())

    def keys(self, tags=None):
        '''Iterate over the keys of all valid items in the cache.'''
        tags = _normalize_tags(tags)
        for key, item in self._store.items():
            if item.valid and (len(tags) == 0 or len(item.tags & tags) > 0):
                yield key

    def values(self, tags=None):
        '''Iterate over the values of all valid items in the cache.'''
        tags = _normalize_tags(tags)
        for item in self._store.values():
            if item.valid and (len(tags) == 0 or len(item.tags & tags) > 0):
                yield item.value

    def items(self, tags=None):
        '''Iterate over all valid items in the cache.'''
        tags = _normalize_tags(tags)
        for key, item in self._store.items():
            if item.valid and (len(tags) == 0 or len(item.tags & tags) > 0):
                yield key, item.value


# Q22. Write a Python program to create and display all combinations of letters, selecting each letter from a different key in a dictionary. Go to the editor
# Sample data : {'1':['a','b'], '2':['c','d']}
# Expected Output:
# ac
# ad
# bc
# bd
data = {'1':['a','b'],'2':['c','d']}
a = []
for i in data :
    a.append(data[i])
j =0
c=0
k = 1
while j < len(a):
    h = 0
    while h < len(a[j]):
        print(a[c][j]+a[k][h])
        h+=1
    j+=1


from popsycle import synthetic
import numpy as np
import matplotlib.pyplot as plt
from astropy.table import Table
import h5py

def test_h5_output(extra_col= True):
    """"
    Parameters
    ----------

     extra_col : boolean, defaults to False
        Tells the code whether or not the new h5 file will have additional columns (ie does the new version of
        popsycle give more information than before
    """

    #find the test files
    test_data_dir = '/u/samrose/scratch/test_files/'
    ebf_file= test_data_dir + 'h5_reference.ebf'
    reference_h5_file= test_data_dir + 'h5_reference.h5'

    #create the new h5 file by running popsycle
    synthetic.perform_pop_syn(ebf_file = ebf_file,
                              output_root = 'test',
                              iso_dir = '/u/casey/scratch/work/microlens/popsycle_test/isochrones/',
                              bin_edges_number = None, overwrite = True, seed=42);

    #read in the data from the reference h5 file
    hfr = h5py.File(reference_h5_file, 'r')
    ref_dset = np.concatenate((hfr['l0b0'], hfr['l0b1'], hfr['l1b0'], hfr['l1b1']),
                              axis=1)
    hfr.close()

    #read in the data from the test h5 file created by popsycle
    hft = h5py.File('test.h5', 'r')
    test_dset = np.concatenate((hft['l0b0'], hft['l0b1'], hft['l1b0'], hft['l1b1']),
                              axis=1)
    hft.close()

    #see if we have the right number of columns
    if test_dset.shape[0] != ref_dset.shape[0] and not extra_col:
            assert test_dset.shape[0] == ref_dset.shape[0], "the h5 files are not the same size. Run again with extra_col=True if you have added columns)"

    #test to see whether the files are the same
    matched_col=0 #initialize matched_col counter
    for i in range(0, ref_dset.shape[0]):
        test_col = test_dset[i,:]
        ref_col = ref_dset[i, :]
        if test_col.all() == ref_col.all():
            matched_col = matched_col+1

        #check to see if disagreements are because of nans
        else:
            bad_idxs = np.where(ref_col != test_col)
            ref_nan_idx = np.where(ref_col == np.nan)
            test_nan_idx = np.where(test_col == np.nan)
            if test_nan_idx.all() == ref_nan_idx.all() and bad_idxs.all() == ref_nan_idx.all():
                matched_col = matched_col+1
            else:
                matched_col= matched_col
                assert test_nan_idx.all() == ref_nan_idx.all(), "Files do not have nan values at the same indices"
                assert bad_idxs.all() == ref_nan_idx.all(), "Coulumns disagree at non-nan values"

    assert matched_col == ref_dset.shape[0], "The new test h5 file does not match the reference file!"

    return


import re
from validate_email import validate_email

TAG_RE = re.compile(r'<[^>]+>')
def remove_tags(text):
    return TAG_RE.sub('', text)

class Email():
  EMAIL_FIELDS = ["to", "from"]
  FIELDS = "to to_name from from_name subject body".split()
  def __init__(self, raw_data):
    self.populate_fields(raw_data)
    self.validate_emails()
    self.sanitize_body()

  def populate_fields(self, raw_data):
    fields = "to to_name from from_name subject body".split()
    for key in self.FIELDS:
      if raw_data.has_key(key) and isinstance(raw_data[key], basestring):
        setattr(self, key, str(raw_data[key]))
      else:
        raise Exception("Error, invalid data for '{}'.".format(key))

  def validate_emails(self):
    for field in self.EMAIL_FIELDS:
      address = getattr(self, field)
      if not validate_email(address):
        raise Exception("Error, invalid email '{}'".format(address))

  def sanitize_body(self):
    self.body = remove_tags(self.body)


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

# In[9]:

import json
with open('file1.json','r') as a:
    data1 = a.read()
obj1 = json.loads(data1)
with open('file2.json','r') as a:
    data2 = a.read()
obj2 = json.loads(data2)
dlt = {i: obj1[i] for i in obj1 if i in obj2 and obj1[i] != obj2[i]}
if len(dlt):
    print ("Есть различие!\nJSON 1 | JSON 2")
    for key, value in dlt.items():
        print (key, "->", value, '|',key, "->", obj2[key])

# In[ ]:


import logging
import threading
import types
from collections import namedtuple
from hashlib import sha256
from time import sleep, time

from goTenna.payload import BinaryPayload, CustomPayload
from termcolor import colored

import config
from utilities import de_segment, naturalsize

logger = logging.getLogger("MSGS")
mesh_logger = logging.getLogger("MESH")

def handle_message(conn, queue):
    """
    Handle messages received over the mesh network
    :param conn: the lntenna.gotenna.Connection instance
    :param queue: a queue.Queue() containing messages
    :return: result of message handling
    """
    while True:
        if queue.empty():
            sleep(0.15)
        else:
            message = queue.get().message

            if isinstance(message.payload, CustomPayload):
                print(message)
            elif isinstance(message.payload, BinaryPayload):
                payload = message.payload._binary_data
                digest = sha256(payload).hexdigest()
                conn.bytes_received += len(payload)
                if config.DEBUG:
                    mesh_logger.info(
                        colored(
                            f"Received {naturalsize(len(payload))} - {digest}", "cyan"
                        )
                    )
                else:
                    mesh_logger.info(
                        colored(f"Received {naturalsize(len(payload))}", "cyan")
                    )
                if not payload[0:4] in config.VALID_MSGS:
                    logger.error(
                        "Message magic not found in VALID_MSGS. Discarding message"
                    )
                    return
                conn.events.send_via_socket.put(payload[4:])
            else:
                payload = message.payload.message
                # test for jumbo:
                jumbo = True if payload.startswith("sm/") else False
                if jumbo:
                    handle_jumbo_message(conn, message)
                    return
                else:
                    logger.error("Unhandled payload type received:")
                    logger.error(payload)

def handle_jumbo_message(conn, message):
    """Handle a jumbo message received.
    """
    payload = message.payload.message
    # TODO: this cuts out all sender and receiver info -- ADD SENDER GID
    logger.info(f"Received jumbo message fragment")
    prefix, seq, length, msg = payload.split("/")

    # if a jumbo monitor thread is not running, start one
    if conn.jumbo_thread.is_alive():
        pass
    else:
        conn.events.jumbo_len = length
        conn.jumbo_thread = None
        conn.jumbo_thread = threading.Thread(
            target=monitor_jumbo_msgs, daemon=True, args=[conn]
        )
        conn.jumbo_thread.start()
    # add the message to the events.jumbo queue
    conn.events.jumbo.append(payload)
    return

def monitor_jumbo_msgs(conn, timeout=210):
    logger.debug("Starting jumbo message monitor thread")
    start = time()
    missing = True
    while True and time() < start + timeout:
        # logger.info(
        #     f"received: {len(conn.events.jumbo)} of {conn.events.jumbo_len} "
        #     f"jumbo messages"
        # )
        if (
            len(conn.events.jumbo) == int(conn.events.jumbo_len)
            and len(conn.events.jumbo) is not 0
        ):
            missing = False
            # give handle_message the attributes it expects
            jumbo_message = types.SimpleNamespace()
            jumbo_message.payload = types.SimpleNamespace()
            # reconstruct the jumbo message
            jumbo_message.payload.message = de_segment(conn.events.jumbo)
            # send it back through handle_message
            logger.info(f"Jumbo message payload reconstituted")
            handle_message(conn, jumbo_message)
            break
        sleep(0.2)
    # reset jumbo events after timeout
    conn.events.init_jumbo()
    if missing:
        logger.error(
            "Did not receive all jumbo messages require for re-assembly. "
            "Please request the message again from the remote host."
        )
    return

"""
Message Structure:

Size    | Description
-----------------------
4       | Magic / Protocol
16      | Host
2       | Port
4       | Checksum / Peer (ID)

This will associate this checksum (peer) with this ip address/port configuration, for
this protocol.

Future messages must all be prefixed with `Checksum`.

Messages not prefixed with a valid Magic or Checksum will be discarded.
"""

# checksums = {}
# Peer = namedtuple("Peer", ["host", "port", "protocol"])
#
#
# def handle_binary_msg(msg):
#     # throw away the message if it's not in magic or the checksum DB
#     prefix = msg[0:4]
#     if prefix not in MAGIC and checksums:
#         print(f"Message prefix unknown: {msg[0:4]}")
#         return
#
#     if prefix in MAGIC:
#         if not len(msg) == 26:
#             print(f"Invalid message length for magic negotiation: {len(msg)}")
#             return
#         # add the host, port, protocol to the peer's entry in checksums
#         checksums[prefix] = Peer(msg[4:20], msg[20:22], msg[0:4])
#         print(f"Peer {prefix} added to in-memory peer dictionary")
#
#     elif prefix in checksums:
#         # if ltng protocol, just strip the header and return it for now
#         if checksums[prefix] == b"ltng":
#             print(f"Peer {prefix}'s message stripped and returned")
#             return msg[4:]


import datetime

def print_progress_bar(curr_time, start_time, stop_time, prefix = '', suffix = '', decimals = 1, length = 100, fill = '█', printEnd = "\r"):
    """
    Call in a loop to create terminal progress bar
    @params:
        curr_time   - Required  : current time (datetime.datetime)
        start_time  - Required  : process start time (datetime.datetime)
        stop_time   - Required  : end time (datetime.datetime)
        prefix      - Optional  : prefix string (Str)
        suffix      - Optional  : suffix string (Str)
        decimals    - Optional  : positive number of decimals in percent complete (Int)
        length      - Optional  : character length of bar (Int)
        fill        - Optional  : bar fill character (Str)
        printEnd    - Optional  : end character (e.g. "\r", "\r\n") (Str)

    Based on: https://stackoverflow.com/questions/3173320/text-progress-bar-in-the-console
    """
    elapsed_time = curr_time - start_time
    process_time = stop_time - start_time
    percent = ("{0:." + str(decimals) + "f}").format(100 * (elapsed_time / process_time))
    filledLength = int(length * elapsed_time // process_time)
    bar = fill * filledLength + '-' * (length - filledLength)
    print('\r%s |%s| %s%% %s' % (prefix, bar, percent, suffix), end = printEnd)
    # Print New Line on Complete
    if curr_time >= stop_time:
        print()


from django.urls import path

from apps.cafes.urls import CAFE_URL_KEYWORD
from apps.products import views

CATEGORY_LIST_URL_NAME = "category-list"
CATEGORY_DETAIL_URL_NAME = "category-detail"
CATEGORY_URL_KEYWORD = "category_id"

OPTION_GROUP_LIST_URL_NAME = "optiongroup-list"
OPTION_GROUP_DETAIL_URL_NAME = "optiongroup-detail"
OPTION_GROUP_URL_KEYWORD = "optiongroup_id"

PRODUCT_LIST_URL_NAME = "product-list"
PRODUCT_DETAIL_URL_NAME = "product-detail"
PRODUCT_URL_KEYWORD = "product_id"

urlpatterns = [
    path(
        f"<uuid:{CAFE_URL_KEYWORD}>/categories/",
        views.CategoryAPIViewSet.as_view({"get": "list", "post": "create"}),
        name=CATEGORY_LIST_URL_NAME,
    ),
    path(
        f"<uuid:{CAFE_URL_KEYWORD}>/categories/<int:{CATEGORY_URL_KEYWORD}>/",
        views.CategoryAPIViewSet.as_view(
            {"get": "retrieve", "put": "update", "delete": "destroy"}
        ),
        name=CATEGORY_DETAIL_URL_NAME,
    ),
    path(
        f"<uuid:{CAFE_URL_KEYWORD}>/optiongroups/",
        views.OptionGroupAPIViewSet.as_view({"get": "list", "post": "create"}),
        name=OPTION_GROUP_LIST_URL_NAME,
    ),
    path(
        f"<uuid:{CAFE_URL_KEYWORD}>/optiongroups/<int:{OPTION_GROUP_URL_KEYWORD}>/",
        views.OptionGroupAPIViewSet.as_view(
            {"get": "retrieve", "put": "update", "delete": "destroy"}
        ),
    ),
    path(
        f"<uuid:{CAFE_URL_KEYWORD}>/products/",
        views.ProductAPIViewSet.as_view({"get": "list", "post": "create"}),
        name=PRODUCT_LIST_URL_NAME,
    ),
    path(
        f"<uuid:{CAFE_URL_KEYWORD}>/products/<int:{PRODUCT_URL_KEYWORD}>/",
        views.ProductAPIViewSet.as_view(
            {"get": "retrieve", "put": "update", "delete": "destroy"}
        ),
        name=PRODUCT_DETAIL_URL_NAME,
    ),
]


import numpy as np
from gym import spaces
import gym
import json
import pickle

class StateNormWrapper(gym.Wrapper):
    """
    Normalize state value for environments.
    """
    def __init__(self, env, file_name):
        super(StateNormWrapper, self).__init__(env)
        with open(file_name, "r") as read_file:
            rl_confs = json.load(read_file)  # hyperparameters for rl training
        print(env.spec.id)
        data_path_prefix = rl_confs["data_collect_confs"]["data_path"]+env.spec.id.split("-")[0].lower()+'/'
        with open(data_path_prefix+'state_info.pkl', 'rb') as f:
            self.state_stats=pickle.load(f)

    def norm(self, s):
        mean =  self.state_stats['mean']
        std = self.state_stats['std']
        s = (s-mean)/std
        return s

    def step(self, a):
        observation, reward, done, info = self.env.step(a)
        return self.norm(observation), reward, done, info

    def reset(self, **kwargs):
        observation = self.env.reset(**kwargs)
        return self.norm(observation)

    def render(self, **kwargs):
        pass

if __name__ == '__main__':
    import matplotlib.pyplot as plt

    # test
    # EnvName = 'CartPole-v1'
    EnvName = 'LunarLander-v2'

    env = StateNormWrapper(gym.make(EnvName), file_name="rl_train.json")

    for _ in range(10):
        env.reset()
        for _ in range(1000):
            # env.render()
            a = env.action_space.sample()
            s, r, d, _ = env.step(a) # take a random action
            if d:
                break
            print(s)
            # print(s.shape)
    env.close()


import numpy as np
import datetime
import math
def anagram(s1,s2):
	s1=list(s1)
	s2=list(s2)
	if len(s1)==(len(s2)):
		s1=set(s1)
		s2=set(s2)
		s3=set()
		if s1^s2==s3:
			print("Anagram")
		else:
			print("not an anagram")
	else:
		print("String are ****NOT*** Anagram")

def primerange(num):
	newarr=[]
	for num in range(0,num+1):
		if num>1:
			for i in range(2,num):
				if num%i==0:
					break
			else:
				#print(num,' ',sep=',',end='')
				newarr.append(num)
	print(newarr)
def primeanagram(num):
	cnt=0
	newarr=[]
	new=[]
	for num in range(0,num+1):
		if num>1:
			for i in range(2,num):
				if num%i==0:
					break
			else:
				#print(num,' ',sep=',',end='')
				newarr.append(num)
	print(newarr)
	for i in range(0,len(newarr)):
		for j in range(i+1,len(newarr)):
			newarr[i]=str(newarr[i])
			newarr[j]=str(newarr[j])
			if len(newarr[i])== len(newarr[j]):
				s1=set(newarr[i])
				s2=set(newarr[j])
				s3=set()
				if s1^s2==s3:
					print(" ******Anagram******")
					cnt+=1
					new.append(s1)
					new.append(s2)
					print("Anagram:-",new)
					print("total count",cnt)
				else:
					print("not an anagram")

			else:
				print("String are ****NOT*** Anagram")
	for i in range(0,len(newarr)):
		newarr[i]=str(newarr[i])
		newarr[j]=newarr[::-1]
		if len(newarr[i])==len(newarr[j]):
			if newarr[i]==newarr[j]:
				print("palindrome")
				cnt+=1
				print(cnt)
			else:
				print()
		else:
			print()
def insertionsort(alist):
	# alist=alist.split(" ")
	for i in range(0,len(alist)):
		print(len(alist))
		current=alist[i]
		while i>0 and alist[i-1]>current:
			alist[i]=alist[i-1]
			i=i-1
			alist[i]=current
	print (alist)

def bubblesort(alist):
	# alist=alist.split(" ")

	for i in range(1,len(alist)):

		for j in range(i):
			if alist[j]>alist[j+1]:
				temp=alist[j]
				alist[j]=alist[j+1]
				alist[j+1]=temp
	print(alist)
	print(len(alist))

def convert(string):
	li=list(string.split(" "))
	return li

def binaryserach(alist,key,length):
	start=0
	end=length-1
	mid=0
	print(start,end)
	while start<=end:
		mid=end//2
		if key == (alist[mid]):
			print("\nEntered number is present at position",key,mid)
			return -1
		elif key<alist[mid]:
			end=mid-1
		elif key > alist[mid]:
			start=mid +1
	print("\n Element not found")

def dayofweek(m,d,y):
	# m=int(input("Enter the month :"))
	# d=int(input("Enter the date :"))
	# y=int(input("Enter the year :"))
	today=datetime.datetime(y,m,d)
	Day=today.weekday()
	print(Day)
	yo=y-(14-m)/12
	x=yo +(yo/4)-(yo/100)+(yo/400)
	print(yo,x)
	mo= m+12*((14-m)/12)-2
	do=(d+x+(31*mo/12))%7
	print(x,mo,do)
	d1=math.floor(do)
	print(d1)
	if Day==0:
		print("Monday")
	elif Day ==1:
		print("Tuesday")
	elif Day ==2:
		print("Wednesday")
	elif Day ==3:
		print("Thursday")
	elif Day ==4:
		print("Friday")
	elif Day ==5:
		print("Saturday")
	else:
		print("Sunday")

	if d1==1:
		print("Monday")
	elif d1 ==2:
		print("Tuesday")
	elif d1 ==3:
		print("Wednesday")
	elif d1 ==4:
		print("Thursday")
	elif d1 ==5:
		print("Friday")
	elif d1 ==6 :
		print("Saturday")
	else:
		print("Sunday")

def tempCon(c,f):
	a=c*9/5 +32
	print("Celsius to fahrenheit: ",a)
	b = (f-32)*5/9
	print("fahrenheit to Celsius: ",b)

def monpay(Y,R,P):
	r=R/(12*100)
	n=Y*12
	p1=P*r
	p2=math.pow(1/(1+r),n)
	p3=1-p2
	print("Enter the number of years in months :- ",n)
	print("Enter the rate of interset ")
	print("Payment to be paid monthly:",p1/p3)
	print("Total amount  to be paid back all together",(p1/p3)*n)
	print(n,r)
	print(p1,p2)

def dectobinary(n):
	binaryarr=[0]*8
	i=0
	while n>0:
		binaryarr[i]=n%2
		n=int(n/2)
		i+=1
	for j in range(7,-1,-1):
		print(binaryarr[j],end=" ")
	return binaryarr
def swap(dec):
	j=7
	for i in range(3,-1,-1):
		temp=dec[i]
		dec[i]=dec[j]
		dec[j]=temp
		j-=1
	print()
	for j in range(7,-1,-1):
		print(dec[j],end=" ")

def bintodec(binaryarr):
	for i in range(0,len(binaryarr)):
		if binaryarr[i]==1:
			k=math.pow(2,i)
			print(k)
		elif binaryarr[i]==0:
			print()

def mergesort(alist):
	if len(alist)>1:
		mid=len(alist)//2
		lefthalf=alist[:mid]
		righthalf=alist[mid:]
		mergesort(lefthalf)
		mergesort(righthalf)
		print(mid)
		print(lefthalf)
		print(righthalf)
		for i in range(1,len(lefthalf)):
			for j in range(i):
				if lefthalf[j]> lefthalf[j+1]:
					temp=lefthalf[j]
					lefthalf[j]=lefthalf[j+1]
					lefthalf[j+1]=temp
					i+=1
		print(lefthalf)
		for i  in range(1,len(righthalf)):
			for j in range(i):
				if righthalf[j] > righthalf[j+1]:
					temp=righthalf[j]
					righthalf[j]=righthalf[j+1]
					righthalf[j+1]=temp
		print(righthalf)
		for i in range(1,len(alist)):
			for j in range(0,i):
				if alist[j]>alist[j+1]:
					temp=alist[j]
					alist[j]=alist[j+1]
					alist[j+1]=temp
	print(alist)

def vendmac(notes):
	print("Amount Enterds into vebding machine",notes)
	no=[]
	n1=[1000,500,200,100,50,20,10,5,2,1]
	i=-1
	while notes>=0:
		if i<len(n1)-1:
			i+=1
		if notes>= n1[i]:
			notes=notes-n1[i]
			print(n1[i])
			i=-1


import random

nomes = ["nome1","nome2","nome3","nome4","nome5","nome6","nome7","nome8","nome9","nome10","nome11","nome12","nome13","nome14","nome15"]
qtd_times = 3
random.shuffle(nomes)
separar_times = [nomes[i::qtd_times] for i in range(qtd_times)]

times = list(separar_times)
indice = 1
for time in times:
    print(f" Time {indice}: {time}")
    indice +=1


from functools import wraps
import time
from utils.mics import colorstr

def fun_run_time(func):
    '''
    装饰器，用于获取函数的执行时间
    放在函数前，如
    @fun_run_time()
    def xxx():
    '''
    @wraps(func)#可删去，是用来显示原始函数名的
    def _inner(*args, **kwargs):
        s_time = time.time()
        ret = func(*args, **kwargs)
        e_time = time.time()
        #
        print(colorstr("\t----function [{}] costs {} s".format(func.__name__, e_time-s_time), 'yellow'))
        return ret
    return _inner

def tic():
    '''
    开始计时。
    t = tic()
    '''
    s_time = time.time()
    return s_time

def toc(s_time, word='tic-toc', act_number = 1, mute=True):
    '''
    结束计时，返回毫秒数。
    t = toc(t, '模块函数名', '处理次数', True)\n
    mute代表不打印。
    '''
    e_time = time.time()
    temp = int((e_time-s_time)*1000)
    if not mute:
        if act_number > 1:
            print(colorstr(f"\t----module [{word}] costs {temp} ms, for {act_number} actions, ({int(temp/act_number)}ms/action)", 'yellow'))
        else:
            print(colorstr(f"\t----module [{word}] costs {temp} ms", 'yellow'))
    return temp


import math

def main():
    times = int(input())
    local_best_length = 0.0000000000
    best_length = 0.0000000000
    for i in range(times): # test cases
        conjunt = int(input())

        for j in range(conjunt): # number of conjunts
            robocopies = int(input())
            list_points = []

            for k in range(robocopies): # number of robocopies
                line = input()
                list_input = line.split()
                x = float(list_input[0])
                y = float(list_input[1])
                list_points.append((x, y))

            for p in range(len(list_points)):
                for p1 in range(p + 1, len(list_points)):
                    d = calc_distance_even(list_points[p], list_points[p1])

                    if d > 0:
                        if d < local_best_length or local_best_length == 0.0:
                            local_best_length = d

            if local_best_length > best_length or best_length == 0.0000000000:
                best_length = local_best_length

            local_best_length = 0.0000000000

        print('{0:.10f}'.format(best_length))

def calc_distance_odd(p1, p2):
    if p1[0] == p2[0]:
        return p1[1] - p2[1]

    if p1[1] == p2[1]:
        return p1[0] - p2[0]

    d = math.sqrt(math.pow((p2[0] - p1[0]), 2) + math.pow((p2[1] - p1[1]), 2))
    return d

def calc_distance_even(p1, p2):
    if p1[0] == p2[0]:
        return p1[1] - p2[1]
    elif p1[1] == p2[1]:
        return p1[0] - p2[0]
    else:
        return 0
main()


# Approach 1: Backtracking with Trie

class Solution:
    def findWords(self, board: List[List[str]], words: List[str]) -> List[str]:
        WORD_KEY = '$'

        trie = {}
        for word in words:
            node = trie
            for letter in word:
                node = node.setdefault(letter, {})
            node[WORD_KEY] = word

        rowNum = len(board)
        colNum = len(board[0])

        matchedWords = []

        def backtrack(row, col, parent):
            letter = board[row][col]
            currNode = parent[letter]

            # check if we find a match
            word_match = currNode.pop(WORD_KEY, False)
            if word_match:
                matchedWords.append(word_match)

            # mark cell as visited
            board[row][col] = '#'

            # explore neighbors in 4 directions
            for rowOffset, colOffset in [(-1, 0), (0, 1), (1, 0), (0, -1)]:
                newRow, newCol = row + rowOffset, col + colOffset
                if newRow < 0 or newRow >= rowNum or newCol < 0 or newCol >= colNum:
                    continue
                if not board[newRow][newCol] in currNode:
                    continue
                backtrack(newRow, newCol, currNode)

            # end of exploration; restore the cell
            board[row][col] = letter

            # Optimization: incrementally remove the matched leaf node in Trie
            if not currNode:
                parent.pop(letter)

        for row in range(rowNum):
            for col in range(colNum):
                if board[row][col] in trie:
                    backtrack(row, col, trie)

        return matchedWords


'''Problem 37: Truncatable primes'''

#g = open('primelist.txt','r')
g = open('primes1.txt','r')
print("g:",type(g),"Opened Prime list. Now reading it...")
h = g.read()
print("h: ",type(h),"Now splitting it into a list...")
j = h.split()
k = [int(x) for x in j]
print("PrimeList is",len(j),"numbers long")
print (k[:10])
primes = [x for x in k if str(x)[0] in ['3','7'] and str(x)[-1] in ['3','7']]
print("The last 10 T-Primes are",primes[-10:])
'''for x in j:
    primes.append(int(x))'''

def isTPrime(n):
    strn = str(n)
    strn = strn.replace(' ','')
    '''if int(strn[0]) in [1,5,9] or int(strn[-1]) in [1,5,9]:
        return False'''
    #print(strn,type(strn))
    for i in range(len(strn)):
        if strn[:i] != '' and strn[i:] != '':
            print(strn[:i])
            if int(strn[:i]) not in k:
                return False
            print(strn[i:])
            if int(strn[i:]) not in k:
                return False
    return True

'''tprimes = [p for p in primes if isTPrime(p)]
tprimesInts = [int(p) for p in tprimes]
print(tprimes,sum(tprimesInts))'''

'''Output:
g: <class '_io.TextIOWrapper'> Opened Prime list. Now reading it...
h:  <class 'str'> Now splitting it into a list...
PrimeList is 1000000 numbers long
[2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
The last 10 T-Primes are [7999727, 7999753, 7999757, 7999787, 7999793, 7999813, 7999847, 7999913, 7999963, 7999993]
[3, 7, 37, 73, 313, 317, 373, 797, 3137, 3797, 739397] 748251
'''


from collections import deque, defaultdict

def bfs(n, adj):
    visited = [False] * (n+1)
    min_dist = [1e9] * (n+1)
    visited[1] = True
    min_dist[1] = 0
    q = deque([1])

    while q:
        cur = q.popleft()

        for a in adj[cur]:
            if not visited[a]:
                q.append(a)
                visited[a] = True
                min_dist[a] = min_dist[cur]+1

    max_dist = max(min_dist[1:])
    return min_dist.count(max_dist)

def solution(n, edge):
    edge.sort()

    adj = defaultdict(list)
    for start, end in edge:
        adj[start].append(end)
        adj[end].append(start)

    return bfs(n, adj)