'''
Created on Nov 16, 2011

@author: jcg
'''

from Features.Feature import Feature
import Functions
from uuid import uuid4

class Motif(Feature):
    """
    Motif Feature
        solution - solution where motif score should be searched
        label - some label to append to the name
        pwm - position weights matrix (PWM) of the motif to be analyzed
        motif_range - start and end position where we should look for the motif - a tuple in the form (start, end)
        mutable_region - a list with all bases that can be mutated
        cds_region - a pair with begin and end of CDSs - example: (0,100)
        keep_aa - boolean option indicating if in the design mode amino acids should be kept
    """
    def __init__(self, motifObject=None, solution=None, label="", args = { 'pwm' : None,
                                                         'motif_range' : (0,9),
                                                         'mutable_region' : None,
                                                         'cds_region' : None,
                                                         'keep_aa' : True }):

        if motifObject == None: #create new instance
            #General properties of feature
            Feature.__init__(self, solution=solution, label=label)
            #Specifics of this Feature
            self.pwm                = args['pwm']
            self.motif_range        = args['motif_range']
            self.sequence           = solution.sequence[self.motif_range[0]:self.motif_range[1]+1]
            self.mutable_region     = [i-self.motif_range[0] for i in set(range(self.motif_range[0],self.motif_range[1]+1)) & set(args['mutable_region'])] if args.has_key('mutable_region') else solution.mutable_region
            self.cds_region         = args['cds_region']    if args.has_key('cds_region') else solution.cds_region
            self.keep_aa            = args['keep_aa']        if args.has_key('keep_aa') else solution.keep_aa
            self.set_scores()
            self.set_level()
        else: #copy instance
            Feature.__init__(self, solution=motifObject.solution, label=motifObject.label)
            self.pwm                = motifObject.pwm
            self.motif_range        = motifObject.motif_range
            self.sequence           = motifObject.sequence
            self.mutable_region     = motifObject.mutable_region
            self.cds_region         = motifObject.cds_region
            self.keep_aa            = motifObject.keep_aa
            self.scores             = motifObject.scores

    def set_scores(self, scoring_function = Functions.analyze_pwm_score):
        self.scores = Functions.appendLabelToDict(scoring_function(self.sequence,self.pwm), self.label)

class MotifScore(Motif):
    """
    Manipulate the motif score
    """
    def __init__(self, motifObject, label = ""):
        Motif.__init__(self,motifObject)
        self.label = self.label + label
        self.set_level()

    def mutate(self, operator=Functions.SimplePWMScoreOperator):
        if not self.targetInstructions:
            return None

        new_seq = list(self.solution.sequence)
        mutated_seq = operator(self.sequence, self.pwm, self.targetInstructions['direction'], self.mutable_region, keep_aa=self.keep_aa)

        if mutated_seq == None:
            return None
        else:
            new_seq[self.motif_range[0]:self.motif_range[1]+1] = list(mutated_seq)
        new_seq = "".join(new_seq)

        return Solution.Solution(sol_id=str(uuid4().int), sequence=new_seq, cds_region = self.cds_region, mutable_region = self.mutable_region, parent=self.solution, design=self.solution.designMethod)

    def defineTarget(self,desiredSolution):
        '''
        Function that determines if a target wasn't hit, and if not updates targetDirections
        '''
        if desiredSolution == None:
            return True

        targetLevel = desiredSolution[self.label+self.__class__.__name__+"Level"]
        currentLevel = self.level

        # Check if there's an associated target position
        if desiredSolution.has_key(self.label+"MotifPositionLevel") and self.solution.levels[self.label+"MotifPositionLevel"] != desiredSolution[self.label+self.__class__.__name__+"Level"]:
            #first we need to set the right position level
            return False
        else:
            if currentLevel == targetLevel:
                return False
            elif currentLevel > targetLevel:
                self.targetInstructions['direction'] = '-' #decrease
            else:
                self.targetInstructions['direction'] = '+' #increase

            return True

class MotifPosition(Motif):
    """
    Manipulate the motif score
    """
    def __init__(self, motifObject, label = ""):
        Motif.__init__(self,motifObject)
        self.label = self.label + label
        self.set_level()

    def defineTarget(self,desiredSolution):
        '''
        Function that determines if a target wasn't hit, and if not updates targetDirections
        '''
        if desiredSolution == None:
            return True

        targetLevel = desiredSolution[self.label+self.__class__.__name__+"Level"]
        currentLevel = self.level

        if currentLevel != targetLevel:
            desiredMotifPosition = self.solution.designMethod.thresholds[self.label+self.__class__.__name__][targetLevel]
            self.targetInstructions['position'] = desiredMotifPosition
            return True
        else:
            return False

    def mutate(self, operator=Functions.SimplePWMPositionOperator):
        if not self.targetInstructions:
            return None

        new_seq = list(self.solution.sequence)
        mutated_seq = operator(self.sequence, self.pwm, self.targetInstructions['position'], self.mutable_region, keep_aa=self.keep_aa)
        if mutated_seq == None:
            return None
        else:
            new_seq[self.motif_range[0]:self.motif_range[1]+1] = list(mutated_seq)
        new_seq = "".join(new_seq)

        return Solution.Solution(sol_id=str(uuid4().int), sequence=new_seq, cds_region = self.cds_region, mutable_region = self.mutable_region, parent=self.solution, design=self.solution.designMethod)

import Solution


import torch
from torch.utils.data import Dataset
from pytorch_pretrained_bert import BertTokenizer

def pad(token_ids, pad_value=0, expected_length=512):
    num_to_pad = expected_length - len(token_ids)
    return token_ids + [pad_value] * num_to_pad

class SentencesDataset(Dataset):
    def __init__(self, data, size=None):
        self.data = data
        self.size = size

        tokenizer = BertTokenizer.from_pretrained('bert-base-uncased', do_lower_case=True)
        self.pad_vid = tokenizer.vocab['[PAD]']

    def __len__(self):
        return self.size or len(self.data)

    def __getitem__(self, idx):
        # src_subtoken_idxs, labels, segments_ids, cls_ids, src_txt, tgt_txt
        input_ids, labels, segments_ids, cls_ids, _, _ = self.data[idx]

        input_t = torch.Tensor(pad(input_ids, pad_value=self.pad_vid)).long()
        segments_t = torch.Tensor(pad(segments_ids)).long()
        cls_t = torch.Tensor(pad(cls_ids, pad_value=-1)).long()
        labels_t = torch.Tensor(pad(labels)).float()
        attention_mask = 1 - (input_t == self.pad_vid)
        cls_mask = 1 - (cls_t == -1)
        return input_t, attention_mask, segments_t, cls_t, cls_mask, labels_t


#!/usr/bin/python

# Melissa Xie, Courtney Sims
# EECE 3230 Final Project
# Prof. Fei, April 2012

# maps available opcodes to their instruction
instr_dict = {'000000':'add',
              '001000':'addi',
              '000100':'beq',
              '100011':'lw',
              '101011':'sw',
              '000010':'j',
              '111111':'hlt'}

def bin_to_int(s):
    """Converts a binary string into an integer representation"""
    return int(s, 2)

class Nop(object):
    """Represents a NOP"""
    pass

class Instruction(object):
    """Represents a MIPS instruction."""
    global instr_dict

    def __init__(self,addr,opcode,rem):
        """Initializes this Instruction."""
        self.addr = addr
        self.opcode = opcode
        self.rem = rem
        self.instr = instr_dict[opcode]
        self.alu_result = None

    def __str__(self):
        """Returns a string representation of this Instruction."""
        return 'addr: %s , opcode: %s'% (self.addr,self.opcode)

class RInstruction(Instruction):
    """Represents an R-type MIPS instruction."""

    def __init__(self,addr,opcode,rem):
        """Initializes this RInstruction."""
        super(RInstruction,self).__init__(addr,opcode,rem)
        self.rs = bin_to_int(rem[:5])
        self.rt = bin_to_int(rem[5:10])
        self.rd = bin_to_int(rem[10:15])
        self.shamt = bin_to_int(rem[15:20])
        self.funct = bin_to_int(rem[20:])
        self.c_signals = { 'RegDst': 1,
                           'ALUSrc': 0,
                           'MemtoReg': 0,
                           'RegWrite': 1,
                           'MemRead': 0,
                           'MemWrite': 0,
                           'Branch': 0,
                           'ALUOp1': 1,
                           'ALUOp0': 0,
                           'Jump': 0 }

    def __str__(self):
        """Returns a string representation of this RInstruction."""
        return '%s %s, %s, %s' % (self.instr, self.rd, self.rs, self.rt)

class IInstruction(Instruction):
    """Represents an I-type MIPS instruction."""

    def __init__(self,addr,opcode,rem):
        """Initializes this IInstruction."""
        super(IInstruction,self).__init__(addr,opcode,rem)
        self.rs = bin_to_int(rem[:5])
        self.rt = bin_to_int(rem[5:10])
        self.imm = bin_to_int(rem[10:]) # do we need to worry about sign extension?
        self.c_signals = self.set_control_signals(self.instr)

    def __str__(self):
        """Returns a string representation of this IInstruction."""
        return '%s %s, %s, %s' % (self.instr, self.rt, self.rs, self.imm)

    def set_control_signals(self,instr):
        """Sets control signals according to the instruction."""
        signals = { 'RegDst': 0,
                    'ALUSrc': 0,
                    'MemtoReg': 0,
                    'RegWrite': 0,
                    'MemRead': 0,
                    'MemWrite': 0,
                    'Branch': 0,
                    'ALUOp1': 0,
                    'ALUOp0': 0,
                    'Jump': 0 }

        if instr == 'lw':
            for s in ['ALUSrc','MemtoReg','RegWrite','MemRead']:
                signals[s] = 1
        elif instr == 'sw':
            for s in ['ALUSrc','MemWrite']:
                signals[s] = 1
        elif instr == 'addi':
            for s in ['ALUSrc','RegWrite']:
                signals[s] = 1
        elif instr == 'beq':
            for s in ['Branch','ALUOp0']:
                signals[s] = 1

        return signals

class JInstruction(Instruction):
    """Represents a J-type MIPS instruction."""

    def __init__(self,addr,opcode,rem):
        """Initializes this JInstruction."""
        super(JInstruction,self).__init__(addr,opcode,rem)
        self.target = bin_to_int(rem)*4
        self.c_signals = { 'RegDst': 0,
                           'ALUSrc': 0,
                           'MemtoReg': 0,
                           'RegWrite': 0,
                           'MemRead': 0,
                           'MemWrite': 0,
                           'Branch': 0,
                           'ALUOp1': 0,
                           'ALUOp0': 0,
                           'Jump': 1 }

    def __str__(self):
        """Returns a string representation of this JInstruction."""
        return '%s %s' % (self.instr, self.target)

class HLTInstruction(Instruction):
    """Represents a HLT."""

    def __init__(self,addr,opcode,rem):
        """Initializes this HLT."""
        super(HLTInstruction,self).__init__(addr,opcode,rem)

    def __str__(self):
        """Returns a string representation of this HLTInstruction."""
        return 'addr: %s , opcode: %s , rem: %s' % (self.addr, self.opcode, self.rem)


# https://pythonguides.com/python-tkinter-table-tutorial/#Python_Tkinter_Table_with_Scrollbar

import tkinter as tk
from  tkinter import ttk

ws  = tk.Tk()
ws.title('Equipment Library')
ws.geometry('640x480')
bgColour = '#292F36'
headerColour = "#4ECDC4"
textColour = "#F7FFF7"
ws['bg'] = bgColour
ttk.Style().configure("Treeview", background=bgColour,
                    foreground=textColour, fieldbackground=bgColour)
window_frame = tk.Frame(ws, background=bgColour)
window_frame.pack()

# Header
l1 = tk.Label(window_frame, text="Scan your card to log in", background=headerColour, foreground=textColour, width="100", height="2", font='Helvetica 14 bold')
l1.pack()

# TextBox Creation
def callback(event):
    global sv
    print(sv.get())
    return
sv = tk.StringVar()
# sv.trace("w", lambda name, index, mode, sv=sv:callback(sv))
e = tk.Entry(window_frame, textvariable=sv)
e.bind("<Enter>",  callback)
e.pack()

#scrollbar
scroller = tk.Scrollbar(window_frame,orient='vertical')
scroller.pack(side=tk.RIGHT, fill=tk.Y)
table = ttk.Treeview(window_frame,yscrollcommand=scroller.set)
table.pack()
scroller.config(command=table.yview)

#define our column
cols = {
    "player_id" : "Id",
    "player_name" : "Name",
    "player_Rank" : "Rank",
    "player_states" : "State",
    "player_city" : "City"
}
table['columns'] = tuple(cols.keys())

# format our columns & headers
table.column("#0", width=0,  stretch=tk.NO)
table.heading("#0",text="",anchor=tk.CENTER)
for ref, text in cols.items():
    table.column(ref,anchor=tk.CENTER, width=80)
    print(ref)
    table.heading(ref,text=text,anchor=tk.CENTER)

#add data
table.insert(parent='',index='end',iid=0,text='',values=('1','Ninja','101','Oklahoma', 'Moore'))
table.insert(parent='',index='end',iid=1,text='',values=('2','Ranger','102','Wisconsin', 'Green Bay'))
table.insert(parent='',index='end',iid=2,text='',values=('3','Deamon','103', 'California', 'Placentia'))
table.insert(parent='',index='end',iid=3,text='',values=('4','Dragon','104','New York' , 'White Plains'))
table.insert(parent='',index='end',iid=4,text='',values=('5','CrissCross','105','California', 'San Diego'))
table.insert(parent='',index='end',iid=5,text='',values=('6','ZaqueriBlack','106','Wisconsin' , 'TONY'))
table.insert(parent='',index='end',iid=6,text='',values=('7','RayRizzo','107','Colorado' , 'Denver'))
table.insert(parent='',index='end',iid=7,text='',values=('8','Byun','108','Pennsylvania' , 'ORVISTON'))
table.insert(parent='',index='end',iid=8,text='',values=('9','Trink','109','Ohio' , 'Cleveland'))
table.insert(parent='',index='end',iid=9,text='',values=('10','Twitch','110','Georgia' , 'Duluth'))
table.insert(parent='',index='end',iid=10,text='',values=('11','Animus','111', 'Connecticut' , 'Hartford'))
table.pack()

ws.mainloop()


"""
 ScharfSandhiArg.py  May 25, 2015
 Jul 26, 2015.
 Jul 27, 2015
 9 May 2020 PMS added parenthesis for python 3 compatibility
"""
from scharfsandhi import ScharfSandhi

if __name__ == '__main__':
 import sys
 sopt = sys.argv[1]
 s = sys.argv[2]
 sandhi = ScharfSandhi()
 sandhi.history=[] # init history.  It is modified by wrapper
 sandhi.dbg=True
 err = sandhi.simple_sandhioptions(sopt)
 if err != 0:
  print("ERROR: sopt must be E, E1, or C, not",sopt)
  exit(1)
 ans = sandhi.sandhi(s)
 for h in sandhi.history:
  print(h)
 print('ScharfSandhiArg: ans="%s"' % ans)


from random import choice

n1 = input("Primeiro aluno: ")
n2 = input("Segundo aluno: ")
n3 = input("Terceiro aluno: ")
n4 = input("Quarto aluno: ")

array = [n1, n2, n3, n4]

print("O escolhido foi: {}".format(choice(array)))


#........................#
#Created By Muhammad Saad#
#on 20/02/2022           #
#........................#

import pandas as pd
import numpy as np

def load_data():
    zeus_1 = pd.read_csv('./Data/Zeus.csv', delimiter=',')
    zeus = zeus_1.to_numpy()
    y_zeus  = np.zeros((len(zeus),2), dtype=int)
    y_zeus[:,0] = 1
    zeus = np.append(zeus,y_zeus, axis=1)

    neris_1 = pd.read_csv('./Data/Neris.csv', delimiter=',')
    neris = neris_1.to_numpy()
    y_neris  = np.zeros((len(neris),2), dtype=int)
    y_neris[:,1] = 1
    neris = np.append(neris,y_neris, axis=1)

    class_names = ["zeus", "neris"]

    X_data = np.concatenate((zeus,neris), axis=0)
    np.random.shuffle(X_data)
    X_new = X_data[:,:784]
    Y = X_data[:,784:]
    my_list = []
    for i in range (X_new.shape[0]):
        my_list.append(X_new[i].reshape(1,28,28))

    X = np.array(my_list)
    return class_names, X, Y


import jsonpickle
from typing import Dict, Union, Optional, List, Type

from Domain.entity import Entity
from Repository.repository import Repository

class JsonRepository(Repository):
    """
    Repository cu fisiere Json
    """

    def __init__(self, filename):
        self.filename = filename

    def __read_file(self):
        """
        Metoda de citire a fisierelor
        :return:
        """
        try:
            with open(self.filename, 'r') as f:
                return jsonpickle.loads(f.read())
        except Exception:
            return {}

    def __write_file(self, objects: Dict[str, Entity]):
        """
        Metoda de scriere in fisier
        :param objects: obiecte
        :return:
        """
        with open(self.filename, 'w') as f:
            f.write(jsonpickle.dumps(objects))

    def create(self, entity: Entity) -> None:
        """
        Metoda de a adauga o entitate
        :param entity:
        :return:
        """

        entities = self.__read_file()
        if self.read(entity.id_entity) is not None:
            raise KeyError(f'Exista deja o '
                           f'entitate cu id-ul {entity.id_entity}.')

        entities[entity.id_entity] = entity
        self.__write_file(entities)

    def read(self, id_entity: object = None) \
            -> Type[Union[Optional[Entity], List[Entity]]]:
        """
        Metoda ce indica entitati
        :param id_entity: id entitate
        :return: toate enttitatile daca nu este specificat un id anume;
        entitatea cu id-ul dat sau None, daca nu exista
        """

        entities = self.__read_file()
        if id_entity:
            if id_entity in entities:
                return entities[id_entity]
            else:
                return None

        return list(entities.values())

    def update(self, entity: Entity) -> None:
        """
        Metoda ce modifica o entitate
        :param entity: o entitate
        :return:
        """
        entities = self.__read_file()
        if self.read(entity.id_entity) is None:
            msg = f'Nu exista o entitate cu id-ul ' \
                  f'{entity.id_entity} de actualizat.'
            raise KeyError(msg)

        entities[entity.id_entity] = entity
        self.__write_file(entities)

    def delete(self, id_entity: str) -> None:
        """
        Metoda de stergere a entitatii
        :param id_entity: id-ul entitatii
        :return:
        """
        entities = self.__read_file()
        if self.read(id_entity) is None:
            raise KeyError(
                f'Nu exista o entitate cu id-ul '
                f'{id_entity} pe care sa o stergem.')

        del entities[id_entity]
        self.__write_file(entities)


#code for caesar cipher encrypt
def cce(au,sk):
    a=au.upper()
    l1=[]
    li="ABCDEFGHIJKLMNOPQRSTUVWXYZ"

    for i in a:
        ctr=0
        if i==" ":
                l1.append(" ")
        for j in li:
            if i==j:
                l1.append(li[ctr+sk])

            ctr+=1
    x=''.join(l1)
    print(x)

#code for caesar cipher decrypt

def ccd(au,sk):
    a=au.upper()
    l1=[]
    li="ZYXWVUTSRQPONMLKJIHGFEDCBA"

    for i in a:
        ctr=0
        if i==" ":
                l1.append(" ")
        for j in li:
            if i==j:
                l1.append(li[ctr+sk])

            ctr+=1
    x=''.join(l1)
    print(x)

#word encrypter
word=input("Enter word : ")
ch=input("Do you want to encrypt or decrypt using Caesar Cipher ? E/D :")

cho=ch.upper()
b=int(input("Provide shift key :"))

if cho=="E":
    #call encrypt function here
    cce(word,b)
elif cho=="D":
    #call decrypt function here
    ccd(word,b)
else:
    print("Invalid input, program shutting down.")


import numpy as np
import os
from matplotlib import pyplot as plt
import cv2
import random
import pickle
import xmltodict
from sklearn.model_selection import train_test_split

DATA_DIR = "archive"

CATEGORIES = ["with_mask", "without_mask", "mask_weared_incorrect"]

IMG_SIZE = 50

training_data = []

images_path = os.path.join(DATA_DIR, "images")
for img in os.listdir(images_path):
    try:
        img_array = cv2.imread(os.path.join(images_path, img), cv2.IMREAD_GRAYSCALE)
        resized = cv2.resize(img_array, (IMG_SIZE, IMG_SIZE))
        annotations_path = "archive/annotations/" + img[:-4] + ".xml"
        with open(annotations_path) as fd:
            info = xmltodict.parse(fd.read())
            label = info['annotation']['object']['name']
            training_data.append([resized, CATEGORIES.index(label)])
    except Exception as e:
        print(e)

random.shuffle(training_data)

X = []
y = []

for features, label in training_data:
    X.append(features)
    y.append(label)

X = np.array(X).reshape(-1, IMG_SIZE, IMG_SIZE, 1)

X_train, X_test, y_train, y_test = train_test_split(X, y, train_size=0.7)

pickle_out = open('X_train.pickle', 'wb')
pickle.dump(X_train, pickle_out)
pickle_out.close()

pickle_out = open('X_test.pickle', 'wb')
pickle.dump(X_test, pickle_out)
pickle_out.close()

pickle_out = open('y_train.pickle', 'wb')
pickle.dump(y_train, pickle_out)
pickle_out.close()

pickle_out = open('y_test.pickle', 'wb')
pickle.dump(y_test, pickle_out)
pickle_out.close()


from bottle import route, run, template, request
import RPi.GPIO as GPIO
import time

IP_ADDRESS = '192.168.0.12' # of your Pi

# Configure the GPIO pin
GPIO.setmode(GPIO.BCM)
RED_PIN = 4

GPIO.setup(RED_PIN, GPIO.OUT)

# Initialize all RGB to OFF
GPIO.output(RED_PIN,GPIO.HIGH)

# Handler for the home page
@route('/')
def index(name='time'):
    return template('home.tpl')

# Handler for the 'rgbled' page
@route('/rgbon', method='POST')
def new_item():
    GPIO.output(RED_PIN,GPIO.LOW)
    return template('home.tpl')

@route('/rgboff', method='POST')
def new_item():
    GPIO.output(RED_PIN,GPIO.HIGH)
    return template('home.tpl')

# Start the webserver running on port 80
try:
    run(host=IP_ADDRESS, port=80)
finally:
    print('Cleaning up GPIO')
    GPIO.cleanup()


from PIL import Image
from bitarray import bitarray
import struct
import sys

class textColors:
	ERROR = '\033[1;31;48m'
	SUCCES = '\033[1;32;48m'

	END = '\033[1;37;48m'

def encode(inFile):
	try:
		inImage = Image.open(inFile)
	except:
		print(textColors.ERROR+"No such file: "+inFile+'!'+textColors.END)
		exit()

	maxMessSize = inImage.size[0]*inImage.size[1]*3/8
	if maxMessSize > 65535:
		maxMessSize = 65535

	print("Max character capacity of "+inFile+" image: " + str(maxMessSize))

	m = input("Enter message to hide: ")

	if len(m) > 65535:
		print(textColors.ERROR+"Message is to long!"+textColors.END)
		exit()

	if maxMessSize < len(m):
		print(textColors.ERROR+"Message is to long!"+textColors.END)
		exit()

	mb = bitarray()
	mb.fromstring(m)
	mbSize = mb.length()

	c = 0 # counter

	oldPixels = inImage.load()

	newImage = Image.new( 'RGB', (inImage.size[0], inImage.size[1]), "black")
	pixels = newImage.load()

	for i in range(newImage.size[0]):    # for every col
		for j in range(newImage.size[1]):    # For every row
			r,g,b = oldPixels[i,j]

			if c < mbSize:
				if mb[c] == 1:
					r = r | 1
				else:
					r = r & 254

				c += 1

			if c < mbSize:
				if mb[c] == 1:
					g = g | 1
				else:
					g = g & 254

				c += 1

			if c < mbSize:
				if mb[c] == 1:
					b = b | 1
				else:
					b = b & 254

				c += 1

			pixels[i,j] = (r,g,b)

	newImage.save("r.png")

	try:
		size = struct.pack('H', len(m)).decode("utf-8")
	except:
		print(textColors.ERROR+"Something went wrong! r.png does not contain hidden message"+textColors.END)
		exit()

	try:
		f = open("r.png", "a+")
	except:
		print(textColors.ERROR+"Something went wrong! r.png does not contain hidden message"+textColors.END)
		exit()

	f.write(size)
	f.close()

	print(textColors.SUCCES+"Succes! Message encrypted in file r.png"+textColors.END)

def decode(inFile):
	try:
		f = open(inFile, "rb")
	except:
		print(textColors.ERROR+"No such file: "+inFile+'!'+textColors.END)
		exit()

	fstr = f.read()
	strLen = fstr[-2:]
	length = struct.unpack('H', strLen)
	length = int(length[0])
	f.close()
	length = length*8

	try:
		inImage = Image.open(inFile)
	except:
		print(textColors.ERROR+"No such file: "+inFile+'!'+textColors.END)
		exit()

	pixels = inImage.load()

	bc = 0 # bit counter
	mb = bitarray() # array for string

	for i in range(inImage.size[0]):    # for every col
		for j in range(inImage.size[1]):    # For every row
			r,g,b = pixels[i,j]

			if bc < length:
				mb.append(r & 1)
				bc+=1
			else:
				break

			if bc < length:
				mb.append(g & 1)
				bc+=1
			else:
				break

			if bc < length:
				mb.append(b & 1)
				bc+=1
			else:
				break

	decodedMessage = mb.tostring()
	print(textColors.SUCCES+"Succes! Decoded message:")
	print(decodedMessage+textColors.END)

def main():
	try:
		operation = sys.argv[1]
	except:
		print(textColors.ERROR+"Incorrect input!"+textColors.END)
		print("Usage: python3 hide.py -e(to encode)/-d(to decode) input_file")
		exit()

	try:
		inFile = sys.argv[2]
	except:
		print(textColors.ERROR+"Incorrect input!"+textColors.END)
		print("Usage: python3 hide.py -e(to encode)/-d(to decode) input_file")
		exit()

	if operation == '-e':
		encode(inFile)
	elif operation == '-d':
		decode(inFile)
	else:
		print(textColors.ERROR+"Incorrect input!"+textColors.END)
		print("Usage: python3 hide.py -e(to encode)/-d(to decode) input_file")
		exit()

if __name__ == "__main__":
    main()


# flake8: noqa
from .base import *
from os.path import abspath, dirname, join

DEBUG = env.bool('DJANGO_DEBUG', default=True)
TEMPLATES[0]['OPTIONS']['debug'] = DEBUG

INSTALLED_APPS += (
    'debug_toolbar',
    'django_extensions',
)

INTERNAL_IPS = ('127.0.0.1',)

# See: https://github.com/django-debug-toolbar/django-debug-toolbar#installation
MIDDLEWARE += (
    'debug_toolbar.middleware.DebugToolbarMiddleware',
)

SECRET_KEY = env('DJANGO_SECRET_KEY',
                 default='7nn(g(lb*8!r_+cc3m8bjxm#xu!q)6fidwgg&$p$6a+alm+x')

EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'

# Use Dummy cache for development
CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.dummy.DummyCache',
    }
}

# Process all tasks synchronously.
# Helpful for local development and running tests
CELERY_EAGER_PROPAGATES_EXCEPTIONS = True
CELERY_ALWAYS_EAGER = True

try:
    from .local import *
except ImportError:
    pass


from word_processor import pre_processing
import math
import pickle
import numpy as np

from add_document import load_files

#function to return the cosine Similarity score of two vectors
def get_cosine_similiarity(terms,document):
    return np.dot(terms,document)/(math.sqrt(np.sum(terms**2))*math.sqrt(np.sum(document**2)))

# function :  process_querry
# parameters  -----------
# 1) words :- string containing the querry
# 2) dic  :- dictionary containing the document lists
# 3) df :- dictionary containing the document frequency of each term
# 4) docs :- list of dictionaries containing the termFrequency of words in each document

def process_querry(words,dic,df,docs):
	# preprocess the querry and convert it into list of valid words
	words = pre_processing(words)

	N = len(dic)

	# dictionary to store the terms in query and their frequency
	terms = {}

	for word in words:
		if word in terms:
			terms[word] += 1
		else:
			terms[word] = 1

	# idfs is list that contains inverse document frequency of each term
	idfs = []
	for term in terms:
		if term in df:
			idfs.append(math.log(N/df[term]))
		else:
			idfs.append(math.log(N))

	# term_vector is the vector that stores the array of the frequency of each term in query
	term_vector = np.fromiter(terms.values(), dtype=float)
	term_vector = term_vector*np.array(idfs)

	# doc_vectors will store the list of all the arrays of frequency of terms in respective document
	doc_vectors = []

	i = 0
	for file in dic:
		tf = docs[file]
		doc_vectors.append([])
		j = 0
		for term in terms:
			if term in tf:
				doc_vectors[i].append(tf[term]*idfs[j])
			else:
				doc_vectors[i].append(idfs[j])
			j += 1
		i += 1
	doc_vectors = np.array(doc_vectors)

	# rank dictionary stores the cosine Similarity score of each document
	rank = {}
	i = 0
	for file in dic:
		rank[file] = get_cosine_similiarity(term_vector,doc_vectors[i])
		i += 1

	# return rank dictionary
	return rank

#process_querry(input('Query : '))
#dic,df,docs = load_files()


from django.conf.urls.defaults import *
from django.views.generic.simple import redirect_to

from .views import (InboxView,
                    OutboxView,
                    MarkSelectedView,
                    UserNewMessagesCount,
                    ViewThread,
                    PlainMessageList)

urlpatterns = patterns('messaging.views',
    url(r'^inbox/$', InboxView.as_view(), name="messaging_inbox"),
    url(r'^$', redirect_to, {'url': 'inbox/'}, name='messaging_redirect'),
    url(r'^compose/$', 'compose', name='messaging_compose'),
    url(r'^compose/(?P<recipient>[a-zA-Z0-9 @_\.,+!?-]+)/$', 'compose', name='messaging_compose_to'),
    url(r'^inbox|outbox/compose/recipient-list/$', 'recipient_typeahead', name='messaging_compose_recipient_list'),
    url(r'^reply/(?P<message_id>[\d]+)/$', 'reply', name='messaging_reply'),
    url(r'^(?P<tab>[\w.]+)/(?P<pk>[\d]+)/$', ViewThread.as_view(), name='view_thread'),
    url(r'^delete/selected/$', MarkSelectedView.as_view(), {'mark': 'delete'}, name='messaging_delete_selected'),
    url(r'^mark/selected/$', MarkSelectedView.as_view(), {'mark': 'read'}, name='messaging_mark_read_selected'),
    url(r'^markunread/selected/$', MarkSelectedView.as_view(), {'mark': 'unread'}, name='messaging_mark_unread_selected'),
    url(r'^markresolved/selected/$', MarkSelectedView.as_view(), {'mark': 'resolved'}, name='messaging_mark_resolved_selected'),
    url(r'^markunresolved/selected/$', MarkSelectedView.as_view(), {'mark': 'unresolved'}, name='messaging_mark_unresolved_selected'),
    url(r'^list/$', PlainMessageList.as_view(), name='messaging_plain_list'),
    url(r'^user/new/messages/$', UserNewMessagesCount.as_view(), name='messaging_new_messages_count'),
    # url(r'^delete/(?P<message_id>[\d]+)/$', delete, name='messaging_delete'),
    # url(r'^undelete/(?P<message_id>[\d]+)/$', undelete, name='messaging_undelete'),
    # url(r'^trash/$', trash, name='messaging_trash'),
)


#link the new intersection points to old intersection points
#0: left_top
#1: left_bottom
#2: right_top
#3: right_bottom

#check wich corners we dont have
def check_corners(corners, side):
	for index in range(0,len(corners)):
		if corners[index] == None:
			corners[index] = make_corners(corners, index, side)
	return corners

#creates the corners we dont have to make the full square
def make_corners(corners, index, side):
	n = 0
	for point in corners:
		if point != None:
			if index == 0:
				if n == 1:
					new_point = (point[0], point[1] - side)
					return new_point
				elif n == 2:
					new_point = (point[0] - side, point[1])
					return new_point
				elif n == 3:
					new_point = (point[0] - side, point[1] - side)
					return new_point
			elif index == 1:
				if n == 0:
					new_point = (point[0], point[1] + side)
					return new_point
				elif n == 2:
					new_point = (point[0] - side, point[1] + side)
					return new_point
				elif n == 3:
					new_point = (point[0] - side, point[1])
					return new_point
			elif index == 2:
				if n == 0:
					new_point = (point[0] + side, point[1])
					return new_point
				if n == 1:
					new_point = (point[0] + side, point[1] - side)
					return new_point
				if n == 3:
					new_point = (point[0], point[1] - side)
					return new_point
			elif index == 3:
				if n == 0:
					new_point = (point[0] + side, point[1] + side)
					return new_point
				if n == 1:
					new_point = (point[0] + side, point[1])
					return new_point
				if n == 2:
					new_point = (point[0], point[1] + side)
					return new_point

		else:
			n += 1

def interpolate(corners, x0, y0, side_old):
	x_values = []
	y_values = []
	for point in corners:
		x_values.append(point[0])
		y_values.append(point[1])

	if len(x_values) > 1:
		x_norm = max(x_values) - min(x_values)
	else:
		x_norm = side_old
	if len(y_values) > 1:
		y_norm = max(y_values) - min(y_values)
	else:
		y_norm = side_old

	norm = (x_norm + y_norm) / 2

	x_delta = x0 - min(x_values)
	y_delta = max(y_values) - y0

	x_est = x_delta / norm
	y_est = y_delta / norm

	return x_est, y_est, norm


import shutil
import tarfile
import tempfile
import threading
import time
import logging
import os

from cpc.network.com.server_connection import ServerConnectionError
from cpc.network.https_connection_pool import ServerConnectionPool, ConnectionPoolEmptyError
from cpc.network.node import Node
from cpc.network.server_to_server_message import ServerToServerMessage
from cpc.server.message.direct_message import PersistentServerMessage
from cpc.server.message.server_message import ServerMessage
from cpc.server.message.direct_message import DirectServerMessage
from cpc.util.conf.server_conf import ServerConf
import projectlist
import cpc.server.queue
import heartbeat
import cpc.server.queue
import cpc.util.plugin
import localassets
import remoteassets
from cpc.util.worker_state import WorkerState
from cpc.server.state.session import SessionHandler
from cpc.network.broadcast_message import BroadcastMessage

log=logging.getLogger(__name__)

class ServerState:
    """Maintains the server state. Must provide synchronized access
       because the server is threaded.

       No threads are to be started in the __init__ method, but rather
       in the startExecThreads method. This is because the ServerState
       is initialized before the server forks. The main process would
       take those threads with it to the grave"""

    def __init__(self, conf):
        self.conf=conf
        self.quit=False
        self.quitlock=threading.Lock()
        self.cmdQueue=cpc.server.queue.CmdQueue()
        self.projectlist=projectlist.ProjectList(conf, self.cmdQueue)
        self.taskExecThreads=None
        self.workerDataList=heartbeat.WorkerDataList()
        self.runningCmdList=heartbeat.RunningCmdList(conf, self.cmdQueue,
                                                     self.workerDataList)
        self.localAssets=localassets.LocalAssets()
        self.remoteAssets=remoteassets.RemoteAssets()
        self.sessionHandler=SessionHandler()
        self.workerStates = dict()
        self.stateSaveThread=None
        self.updateThread = None
        self.keepAliveThread = None
        self.reestablishConnectionThread = None

        self.readableSocketLock = threading.Lock()
        self.readableSockets = []

    def startExecThreads(self):
        """Start the exec threads."""
        self.taskExecThreads=cpc.server.queue.TaskExecThreads(self.conf, 1,
                                                self.projectlist.getTaskQueue(),
                                                self.cmdQueue)
        self.stateSaveThread=threading.Thread(target=stateSaveLoop,
                                              args=(self, self.conf, ))
        self.stateSaveThread.daemon=True
        self.stateSaveThread.start()
        log.debug("Starting state save thread.")
        self.runningCmdList.startHeartbeatThread()

    def startConnectServerThread(self):
        self.connectServerThread=threading.Thread(
                                            target=establishConnections,
                                            args=(self,)
                                            ,name="ConnectServerThread")
        self.connectServerThread.daemon=True
        self.connectServerThread.start()

    def startKeepAliveThread(self):
         self.keepAliveThread=threading.Thread(target=sendKeepAlive,
                                                 args=(self.conf, )
                                                ,name="KeepAliveThread")
         self.keepAliveThread.daemon=True
         self.keepAliveThread.start()

    def startReestablishConnectionThread(self):
        self.reestablishConnectionThread=threading.Thread(target=reestablishConnections,
            args=(self, ),name="reestablishConnectionThread")
        self.reestablishConnectionThread.daemon=True
        self.reestablishConnectionThread.start()

    def doQuit(self):
        """set the quit state to true"""
        with self.quitlock:
            self.taskExecThreads.stop()
            self._write()
            self.quit=True
            doProfile = self.conf.getProfiling()
            if doProfile:
                try:
                    import yappi
                    logDir = self.conf.getLogDir()
                    profFile = os.path.join(logDir, 'server_profile.call')
                    yappi.get_func_stats().save(profFile, 'callgrind')
                    profFile = os.path.join(logDir, 'server_profile.txt')
                    profFileF = open(profFile, 'w')
                    yappi.get_func_stats().print_all(profFileF)
                    profFileF.close()
                except:
                    log.exception('Cannot write profiling information')

    def getQuit(self):
        """get the quit state"""
        with self.quitlock:
            ret=self.quit
        return ret

    def getLocalAssets(self):
        """Get the localassets object"""
        return self.localAssets

    def getRemoteAssets(self):
        """Get the remoteassets object"""
        return self.remoteAssets

    def getSessionHandler(self):
        """Get the session handler"""
        return self.sessionHandler

    def getProjectList(self):
        """Get the list of projects as an object."""
        return self.projectlist

    def getCmdQueue(self):
        """Get the run queue as an object."""
        return self.cmdQueue

    def getRunningCmdList(self):
        """Get the running command list."""
        return self.runningCmdList

    def getWorkerDataList(self):
        """Get the worker directory list."""
        return self.workerDataList

    def getCmdLocation(self, cmdID):
        """Get the argument command location."""
        return  self.runningCmdList.getLocation(cmdID)

    def write(self):
        """Write the full server state out to all appropriate files."""
        # we go through all these motions to make sure that nothing prevents
        # the server from starting up again.
        self.taskExecThreads.acquire()
        try:
            self.taskExecThreads.pause()
            self._write()
            self.taskExecThreads.cont()
        finally:
            self.taskExecThreads.release()

    def saveProject(self,project):
        self.taskExecThreads.acquire()
        conf = ServerConf()
        try:
            self.taskExecThreads.pause()
            self._write()
            projectFolder = "%s/%s"%(conf.getRunDir(),project)
            if(os.path.isdir(projectFolder)):
                #tar the project folder but keep the old files also, this is
                # only a backup!!!
                #copy _state.xml to _state.bak.xml
                stateBackupFile = "%s/_state.bak.xml"%projectFolder
                shutil.copyfile("%s/_state.xml"%projectFolder,stateBackupFile)
                tff=tempfile.TemporaryFile()
                tf=tarfile.open(fileobj=tff, mode="w:gz")
                tf.add(projectFolder, arcname=".", recursive=True)
                tf.close()
                del(tf)
                tff.seek(0)
                os.remove(stateBackupFile)
                self.taskExecThreads.cont()
            else:
                self.taskExecThreads.cont()
                raise Exception("Project does not exist")
        finally:
            self.taskExecThreads.release()

        return tff

    def _write(self):
        self.projectlist.writeFullState(self.conf.getProjectFile())
        #self.taskQueue.writeFullState(self.conf.getTaskFile())
        #self.projectlist.writeState(self.conf.getProjectFile())
        self.runningCmdList.writeState()

    def read(self):
        self.projectlist.readState(self, self.conf.getProjectFile())
        self.runningCmdList.readState()

    #rereads the project state for one specific project
    def readProjectState(self,projectName):
        self.projectlist.readProjectState(projectName)

    def getWorkerStates(self):
        '''
        returns: dict
        '''
        return self.workerStates

    def setWorkerState(self,state,workerId,originating):
        # we construct the object first as the key is dependant on the id
        # generated by the constructor. Not thread safe
        workerState = cpc.util.worker_state.WorkerState(originating,state,workerId)
        if workerState.workerId in self.workerStates:
            self.workerStates[workerState.workerId].setState(state)
        else:
            self.workerStates[workerState.workerId] = workerState

    def addReadableSocket(self,socket):
        with self.readableSocketLock:
            self.readableSockets.append(socket)

    def removeReadableSocket(self,socket):
        with self.readableSocketLock:
            self.readableSockets.remove(socket)

def stateSaveLoop(serverState, conf):
    """Function for the state saving thread."""
    while True:
        time.sleep(conf.getStateSaveInterval())
        if not serverState.getQuit():
            log.debug("Saving server state.")
            serverState.write()

def establishConnections(serverState):
    establishInboundConnections(serverState)
    establishOutBoundConnections()
    serverState.startKeepAliveThread()
    serverState.startReestablishConnectionThread()

def establishOutboundConnection(node):
    conf = ServerConf()

    for i in range(0, conf.getNumPersistentConnections()):
        try:
            #This will make a regular call, the connection pool will take
            # care of the rest
            message = PersistentServerMessage(node, conf)
            resp = message.persistOutgoingConnection()
            node.addOutboundConnection()

        except ServerConnectionError as e:
            #The node is not reachable at this moment,
            # no need to throw an exception since we are marking the node
            # as unreachable in ServerConnection
            log.log(cpc.util.log.TRACE, "Exception when establishing "
                                        "outgoing connections: %s " % e)
            break

    if node.isConnectedOutbound():
        log.log(cpc.util.log.TRACE,"Established outgoing "
                                   "connections to server "
                                   "%s"%node.toString())

    else:
        log.log(cpc.util.log.TRACE,"Could not establish outgoing "
                                   "connections to %s"%node.toString())
def establishOutBoundConnections():
    conf = ServerConf()

    log.log(cpc.util.log.TRACE,"Starting to establish outgoing connections")

    for node in conf.getNodes().nodes.itervalues():
        establishOutboundConnection(node)

    log.log(cpc.util.log.TRACE,"Finished establishing outgoing "
                               "connections")

def establishInboundConnection(node, serverState):
    conf=ServerConf()

    for i in range(0, conf.getNumPersistentConnections()):
        try:
            message = PersistentServerMessage(node, conf)
            socket = message.persistIncomingConnection()
            serverState.addReadableSocket(socket)
            node.addInboundConnection()

        except ServerConnectionError as e:
            #The node is not reachable at this moment,
            # no need to throw an exception since we are marking the node
            # as unreachable ins ServerConnectionHandler
            log.log(cpc.util.log.TRACE, "Exception when establishing "
                                        "inbound connections: %s " % e)
            break

    if node.isConnectedInbound():
        log.log(cpc.util.log.TRACE, "Established inbound "
                                "connections to server "
                                "%s" % node.toString())
    else:
        log.log(cpc.util.log.TRACE, "Could not establish inbound "
                                "connections to server "
                                "%s" % node.toString())

def establishInboundConnections(serverState):
    """
    for each node that is not connected
    try to establish an inbound connection
    """
    conf = ServerConf()
    log.log(cpc.util.log.TRACE,"Starting to establish incoming connections")
    for node in conf.getNodes().nodes.itervalues():
        establishInboundConnection(node, serverState)

    log.log(cpc.util.log.TRACE,"Finished establishing incoming "
                                   "connections")

def sendKeepAlive(conf):
    """
        Sends a message for each connected node in order to keep the
        connection alive
    """
    log.log(cpc.util.log.TRACE,"Starting keep alive thread")

    #first get the network topology. by doing this we know that the network
    # topology is fetched and resides in the cache.
    #since we are later on fetching all connections from the connection pool
    # there will be no connection left to do this call thus we must be sure
    # that its already cached.
    ServerToServerMessage.getNetworkTopology()

    while True:
        log.log(cpc.util.log.TRACE,"Starting to send keep alive")
        sentRequests = 0
        for node in conf.getNodes().nodes.itervalues():
            if node.isConnectedOutbound():
                try:
                    connections = ServerConnectionPool().getAllConnections(node)

                    for conn in connections:
                        try:
                            message = DirectServerMessage(node,conf)
                            message.conn = conn
                            message.pingServer(node.getId())
                            log.log(cpc.util.log.TRACE,"keepAlive sent to %s"%node
                            .toString())

                        except ServerConnectionError:
                            log.error("Connection to %s is broken"%node.toString())

                    sentRequests+=1

                except ConnectionPoolEmptyError:
                    #this just mean that no connections are available in the
                    # pool i.e they are used and communicated on thus we do
                    # not need to send keep alive messages on them
                    continue

        keepAliveInterval = conf.getKeepAliveInterval()
        log.log(cpc.util.log.TRACE,"sent keep alive to %s nodes "
                                   "will resend in %s seconds"%(sentRequests,
                                                         keepAliveInterval))
        time.sleep(keepAliveInterval)

def reestablishConnections(serverState):
    '''
    Tries to periodically check for nodes that have gone down and reestablish
     connections to them
    '''
    log.log(cpc.util.log.TRACE,"Starting reestablish connection thread")
    conf = ServerConf()
    while True:
        for node in conf.getNodes().nodes.itervalues():
            if not node.isConnected():
                establishInboundConnection(node,serverState)
                establishOutboundConnection(node)

            if not node.isConnected():
                log.log(cpc.util.log.TRACE,("Tried to reestablish a "
                                            "connection"
                                            " to %s but failed "%node.toString()))

        reconnectInterval = conf.getReconnectInterval()
        time.sleep(reconnectInterval)


from pycuda import gpuarray
import pycuda.driver as drv
from pycuda.elementwise import ElementwiseKernel
import numpy as np

from imagingtester import (
    MINIMUM_PIXEL_VALUE,
    MAXIMUM_PIXEL_VALUE,
    create_arrays,
    DTYPE,
    SIZES_SUBSET,
    N_RUNS,
)
from numpy_scipy_imaging_filters import numpy_background_correction
from pycuda_test_utils import PyCudaImplementation, C_DTYPE, LIB_NAME

from write_and_read_results import (
    ARRAY_SIZES,
    write_results_to_file,
    ADD_ARRAYS,
    BACKGROUND_CORRECTION,
)

mode = "elementwise kernel"

BackgroundCorrectionKernel = ElementwiseKernel(
    arguments="{0} * data, {0} * flat, const {0} * dark, const {0} MINIMUM_PIXEL_VALUE, const {0} MAXIMUM_PIXEL_VALUE".format(
        C_DTYPE
    ),
    operation="flat[i] -= dark[i];"
    "if (flat[i] == 0) flat[i] = MINIMUM_PIXEL_VALUE;"
    "data[i] -= dark[i];"
    "data[i] /= flat[i];"
    "if (data[i] > MAXIMUM_PIXEL_VALUE) data[i] = MAXIMUM_PIXEL_VALUE;"
    "if (data[i] < MINIMUM_PIXEL_VALUE) data[i] = MINIMUM_PIXEL_VALUE;",
    name="BackgroundCorrectionKernel",
)

elementwise_background_correction = lambda data, flat, dark: BackgroundCorrectionKernel(
    data, flat, dark, MINIMUM_PIXEL_VALUE, MAXIMUM_PIXEL_VALUE
)

# Create an element-wise Add Array Function
AddArraysKernel = ElementwiseKernel(
    arguments="{0} * arr1, {0} * arr2".format(C_DTYPE),
    operation="arr1[i] += arr2[i]",
    name="AddArraysKernel",
)

class PyCudaKernelImplementation(PyCudaImplementation):
    def __init__(self, size, dtype):

        super().__init__(size, dtype)
        self.warm_up()

    def warm_up(self):
        warm_up_size = (1, 1, 1)
        cpu_arrays = create_arrays(warm_up_size, DTYPE)
        gpu_arrays = self._send_arrays_to_gpu(cpu_arrays)
        BackgroundCorrectionKernel(
            gpu_arrays[0],
            gpu_arrays[1],
            gpu_arrays[2],
            MINIMUM_PIXEL_VALUE,
            MAXIMUM_PIXEL_VALUE,
        )
        AddArraysKernel(gpu_arrays[0], gpu_arrays[1])

practice_array = np.ones(shape=(5, 5, 5)).astype(DTYPE)
practice_array = gpuarray.to_gpu(practice_array)
AddArraysKernel(practice_array, practice_array)
assert np.all(practice_array.get() == 2)

np_arrays = [np.random.uniform(low=0.0, high=20, size=(5, 5, 5)) for _ in range(3)]
np_data, np_dark, np_flat = np_arrays
cuda_data, cuda_dark, cuda_flat = [gpuarray.to_gpu(np_arr) for np_arr in np_arrays]
elementwise_background_correction(cuda_data, cuda_flat, cuda_dark)
numpy_background_correction(np_dark, np_data, np_flat)
assert np.allclose(np_data, cuda_data.get())

add_arrays_results = []
background_correction_results = []

for size in ARRAY_SIZES[:SIZES_SUBSET]:

    imaging_obj = PyCudaKernelImplementation(size, DTYPE)

    avg_add = imaging_obj.timed_imaging_operation(N_RUNS, AddArraysKernel, "adding", 2)
    avg_bc = imaging_obj.timed_imaging_operation(
        N_RUNS, elementwise_background_correction, "background correction", 3
    )

    if avg_add > 0:
        add_arrays_results.append(avg_add)
    if avg_bc > 0:
        background_correction_results.append(avg_bc)

write_results_to_file([LIB_NAME, mode], ADD_ARRAYS, add_arrays_results)
write_results_to_file(
    [LIB_NAME, mode], BACKGROUND_CORRECTION, background_correction_results
)

drv.Context.pop()


import numpy as np
import theano
import theano.tensor as T
import deep
import autoencoder
import tools

class MlpModel(object):
    def __init__(self,hidden,logistic):
        self.hidden=hidden
        self.logistic=logistic

    def get_params(self):
        return self.hidden.get_params() + self.logistic.get_params()

def built_nn_cls(hyper_params):
    model= create_mlp_model(hyper_params)
    free_vars=deep.LabeledImages()
    train,test,prob_dist=create_nn_fun(free_vars,model,hyper_params)
    return deep.Classifier(free_vars,model,train,test,prob_dist)

def create_mlp_model(hyper_params):
    n_in=hyper_params['n_in']
    n_hidden=hyper_params['n_hidden']
    n_out=hyper_params['n_out']
    rand=deep.RandomNum()
    hidden_shape=(n_in,n_hidden)
    hidden=deep.create_layer(hidden_shape,rand,"_hidden")
    vis_shape=(n_hidden,n_out)
    logistic=deep.create_layer(vis_shape,rand,"_vis")
    return MlpModel(hidden,logistic)

def create_nn_fun(free_vars,model,hyper_params):
    learning_rate=hyper_params['learning_rate']
    py_x=get_px_y(free_vars,model)
    loss=tools.get_loss_function(free_vars,py_x)
    return tools.construct_functions(free_vars,model,py_x,loss,learning_rate)

def get_px_y(free_vars,model):
    hidden=model.hidden
    output_layer=model.logistic
    h = T.nnet.sigmoid(T.dot(free_vars.X, hidden.W) + hidden.b)
    pyx = T.nnet.softmax(T.dot(h, output_layer.W) + output_layer.b)
    return pyx


deck_of_cards = input().split(", ")
number = int(input())

for num in range(number):
    command = input().split(", ")
    action = command[0]
    if action == "Add":
        card_name = command[1]
        if card_name not in deck_of_cards:
            deck_of_cards.append(card_name)
            print("Card successfully added")
        else:
            print("Card is already in the deck")
    elif action == "Remove":
        name_of_card = command[1]
        if name_of_card in deck_of_cards:
            deck_of_cards.remove(name_of_card)
            print("Card successfully removed")
        else:
            print("Card not found")
    elif action == "Remove At":
        given_index = int(command[1])
        if 0 <= given_index < len(deck_of_cards):
            deck_of_cards.pop(given_index)
            print("Card successfully removed")
        else:
            print("Index out of range")
    elif action == "Insert":
        index1 = int(command[1])
        name_card = command[2]
        if 0 <= index1 < len(deck_of_cards):
            if name_card not in deck_of_cards:
                deck_of_cards.insert(index1, name_card)
                print("Card successfully added")
            else:
                print("Card is already added")
        else:
            print("Index out of range")

print(", ".join(deck_of_cards))


# LIBS
# Custom libs
from src.utils import last_month, open_file
from mlocker_components.locker import Locker

# Base libs
import shutil
import pandas as pd

from datetime import datetime
from google.cloud import bigquery

# Airlow libs
from airflow.settings import AIRFLOW_HOME
from airflow.models import DAG, Variable
from airflow.operators.python import PythonOperator

# SETTINGS
CHAT_ID = 532076235 # test id
PERIOD_OF_REPORT = tuple(map(str, last_month()))

PLATFORMS_BQ = {'android': 'ruligastavokandroid_mob2', 'ios': 'id1065803457'}
PLATFORMS_AF = {'android': 'ru.ligastavok.android-mob2', 'ios': 'id1065803457'}

# Paths
SOURCE_DIR = f'{AIRFLOW_HOME}/data/{PERIOD_OF_REPORT[1]}'
RESULT_DIR = f'{AIRFLOW_HOME}/result/{PERIOD_OF_REPORT[1]}'

SOURCE_PATHS = {
    'ru android event': f'{SOURCE_DIR}/ru/{PLATFORMS_BQ["android"]}_events_{PERIOD_OF_REPORT[0]}_{PERIOD_OF_REPORT[1]}.csv',
    'ru ios event': f'{SOURCE_DIR}/ru/{PLATFORMS_BQ["ios"]}_events_{PERIOD_OF_REPORT[0]}_{PERIOD_OF_REPORT[1]}.csv',
    'all android event': f'{SOURCE_DIR}/all/{PLATFORMS_BQ["android"]}_events_{PERIOD_OF_REPORT[0]}_{PERIOD_OF_REPORT[1]}.csv',
    'all ios event': f'{SOURCE_DIR}/all/{PLATFORMS_BQ["ios"]}_events_{PERIOD_OF_REPORT[0]}_{PERIOD_OF_REPORT[1]}.csv',

    'ru android fraud': f'{SOURCE_DIR}/ru/{PLATFORMS_AF["android"]}_frauds_{PERIOD_OF_REPORT[0]}_{PERIOD_OF_REPORT[1]}.csv',
    'ru ios fraud': f'{SOURCE_DIR}/ru/{PLATFORMS_AF["ios"]}_frauds_{PERIOD_OF_REPORT[0]}_{PERIOD_OF_REPORT[1]}.csv',
    'all android fraud': f'{SOURCE_DIR}/all/{PLATFORMS_AF["android"]}_frauds_{PERIOD_OF_REPORT[0]}_{PERIOD_OF_REPORT[1]}.csv',
    'all ios fraud': f'{SOURCE_DIR}/all/{PLATFORMS_AF["ios"]}_frauds_{PERIOD_OF_REPORT[0]}_{PERIOD_OF_REPORT[1]}.csv'
}

RESULT_DIRS = {
    'ru report new': f'{RESULT_DIR}/RU_report_{PERIOD_OF_REPORT[0]}_{PERIOD_OF_REPORT[1]}_NEW',
    'ru report old': f'{RESULT_DIR}/RU_report_{PERIOD_OF_REPORT[0]}_{PERIOD_OF_REPORT[1]}_OLD',
    'all report new': f'{RESULT_DIR}/ALL_report_{PERIOD_OF_REPORT[0]}_{PERIOD_OF_REPORT[1]}_NEW',
    'all report old': f'{RESULT_DIR}/ALL_report_{PERIOD_OF_REPORT[0]}_{PERIOD_OF_REPORT[1]}_OLD',

    'ru imgs new': f'{RESULT_DIR}/RU_imgs_{PERIOD_OF_REPORT[0]}_{PERIOD_OF_REPORT[1]}_NEW',
    'ru imgs old': f'{RESULT_DIR}/RU_imgs_{PERIOD_OF_REPORT[0]}_{PERIOD_OF_REPORT[1]}_OLD',
    'all imgs new': f'{RESULT_DIR}/ALL_imgs_{PERIOD_OF_REPORT[0]}_{PERIOD_OF_REPORT[1]}_NEW',
    'all imgs old': f'{RESULT_DIR}/ALL_imgs_{PERIOD_OF_REPORT[0]}_{PERIOD_OF_REPORT[1]}_OLD'
}

# Keys
TG_TOKEN = Variable.get('tg_mlocker_token')
AF_TOKEN = Variable.get('appsflyer_api_key')
BQ_TOKEN = Variable.get('wa-service-account_secret', deserialize_json=True)

# INITS
bigclient = bigquery.Client.from_service_account_info(BQ_TOKEN)
bot = Locker(
    chat_id=CHAT_ID,
    period=PERIOD_OF_REPORT,
    tg_token=TG_TOKEN,
    af_token=AF_TOKEN,
    bigclient = bigclient
)

def create_folders():
    bot.send_message(f'Собираю данные для отчета...\nпериод c {PERIOD_OF_REPORT[0]} по {PERIOD_OF_REPORT[1]}')
    bot.create_dirs(AIRFLOW_HOME, 'data', 'result')
    print('folders created\n')

def collect_events():
    bot.collect_events(PLATFORMS_BQ["android"], 'ru').to_csv(SOURCE_PATHS['ru android event'], index=False)
    bot.collect_events(PLATFORMS_BQ["ios"], 'ru').to_csv(SOURCE_PATHS['ru ios event'], index=False)
    bot.collect_events(PLATFORMS_BQ["android"], 'all').to_csv(SOURCE_PATHS['all android event'], index=False)
    bot.collect_events(PLATFORMS_BQ["ios"], 'all').to_csv(SOURCE_PATHS['all ios event'], index=False)
    print('events collected\n')

def collect_frauds():
    rows = 100_000
    open_file(SOURCE_PATHS['ru android fraud'], 'w', text=bot.collect_frauds(PLATFORMS_AF["android"], 'ru', rows))
    open_file(SOURCE_PATHS['ru ios fraud'], 'w', text=bot.collect_frauds(PLATFORMS_AF["ios"], 'ru', rows))
    open_file(SOURCE_PATHS['all android fraud'], 'w', text=bot.collect_frauds(PLATFORMS_AF["android"], 'all', rows))
    open_file(SOURCE_PATHS['all ios fraud'], 'w', text=bot.collect_frauds(PLATFORMS_AF["ios"], 'all', rows))
    print('fraud collected\n')

def process_reports():
    bot.send_message(f'Готовлю отчет...')

    # Get data
    source_dfs_ru = {}
    source_dfs_all = {}

    for key, path in SOURCE_PATHS.items():
        df = pd.read_csv(path)
        df.columns = df.columns.str.replace(' ', '_')
        if 'ru ' in key:
            source_dfs_ru[key] = df[df['Country_Code'] == 'RU']
        elif 'all ' in key:
            source_dfs_all[key] = df

    print('source dfs loaded\n')

    # Process data
    processed_dfs_ru = bot.process(source_dfs_ru)
    processed_dfs_all = bot.process(source_dfs_all)

    # Assembling report
    bot.report(processed_dfs_ru['new_attr'].get_dataframe(), RESULT_DIRS['ru report new'], RESULT_DIRS['ru imgs new'])
    bot.report(processed_dfs_ru['old_attr'].get_dataframe(), RESULT_DIRS['ru report old'], RESULT_DIRS['ru imgs old'])
    bot.report(processed_dfs_all['new_attr'].get_dataframe(), RESULT_DIRS['all report new'], RESULT_DIRS['all imgs new'])
    bot.report(processed_dfs_all['old_attr'].get_dataframe(), RESULT_DIRS['all report old'], RESULT_DIRS['all imgs old'])
    print('report assembled\n')

def push_reports():
    bot.split_reports(RESULT_DIR)
    print('Report splited')

    bot.archive(RESULT_DIR)
    print('Report archived')

    bot.send_message('Загружаю файлы...')
    bot.send_documents(RESULT_DIR)
    bot.send_message('Готово!')
    print('Report pushed')

    shutil.rmtree(f'{SOURCE_DIR}')
    shutil.rmtree(f'{RESULT_DIR}')
    print('Dirs was cleared')

with DAG(
    'mobile_locker_bot',
    schedule_interval='0 6 1 * *',
    start_date=datetime(2022, 8, 8),
    default_args={
        'owner': 'Sorokin Yegor',
        'email': 'egor.sorokin@ligastavok.ru',
        'email_on_failure': True,
        'depends_on_past': True
    },
    max_active_runs=1,
    tags=['Mobile', 'Telegram', 'Bot']
) as dag:

    # Init operators
    folders = PythonOperator(task_id='create_folders', python_callable=create_folders)
    events = PythonOperator(task_id='collect_events', python_callable=collect_events)
    frauds = PythonOperator(task_id='collect_frauds', python_callable=collect_frauds)
    process = PythonOperator(task_id='process_reports', python_callable=process_reports)
    push = PythonOperator(task_id='push_reports', python_callable=push_reports)

    # Pipline
    folders.set_downstream(events)
    folders.set_downstream(frauds)

    events.set_downstream(process)
    frauds.set_downstream(process)

    process.set_downstream(push)


import itertools
import json
import os
from typing import Literal, Optional

import networkx as nx
import numpy as np
import pandas as pd
import plotly.express as px
import torch
from PIL.Image import Image
from pingouin import distance_corr
from sklearn.manifold import TSNE
from thefuzz import process as fuzz_process
from torch import nn, optim
from torch.utils.data import DataLoader, Dataset, Sampler
from tqdm import tqdm
from transformers import (
    AutoProcessor,
    AutoTokenizer,
    CLIPTextModelWithProjection,
    CLIPVisionModelWithProjection,
)

from preprocessing import process_image

class KGDatasetBatchSampler(Sampler):
    def __init__(self, data_source: "KGDataset", batch_size: int) -> None:
        super().__init__(data_source)
        self.data_source = data_source
        self.batch_size = batch_size
        text_text = torch.randperm(len(self.data_source.text_text_pairs)).tolist()
        text_image = (
            torch.randperm(len(self.data_source.text_image_pairs))
            + len(self.data_source.text_text_pairs)
        ).tolist()
        image_text = (
            torch.randperm(len(self.data_source.image_text_pairs))
            + len(self.data_source.text_text_pairs)
            + len(self.data_source.text_image_pairs)
        ).tolist()
        image_image = (
            torch.randperm(len(self.data_source.image_image_pairs))
            + len(self.data_source.text_text_pairs)
            + len(self.data_source.text_image_pairs)
            + len(self.data_source.image_text_pairs)
        ).tolist()
        batches = (
            self.__to_chuncks(text_text)
            + self.__to_chuncks(text_image)
            + self.__to_chuncks(image_text)
            + self.__to_chuncks(image_image)
        )
        np.random.shuffle(batches)
        self.batches = batches

    def __iter__(self):
        for batch in self.batches:
            yield batch

    def __to_chuncks(self, lst):
        chuncks = []
        for i in range(0, len(lst), self.batch_size):
            chuncks.append(lst[i : i + self.batch_size])
        return chuncks

    def __len__(self):
        return len(self.batches)

class KGDataset(Dataset):
    def __init__(
        self,
        kg_csv_path: str,
        face_images_dir: Optional[str] = None,
        max_pairs: int = 1000,
        max_hops: int = 3,
    ):
        """
        Create a dataset of pairs of entities from a knowledge graph.

        Parameters:
        ----------
        kg_csv_path : the path to the CSV file containing the knowledge graph
        face_images_dir : the path to the directory containing the face images of film characters
        max_pairs : the maximum number of pairs to generate
        max_hops : the maximum number of hops to consider for symmetric relations
        """

        kg_df = self.__load_knowledge_graph_with_embeddings(kg_csv_path)
        kg = self.__kg_df_to_graph(kg_df)
        pair_gen = self.__build_pairs(kg, num_hops=max_hops)
        textual_pairs = []
        for _ in tqdm(range(max_pairs), total=max_pairs):
            try:
                textual_pairs.append(next(pair_gen))
            except StopIteration:
                break

        # filter out duplicate pairs, under symmetric relations
        textual_pairs = list({tuple(sorted(pair)) for pair in textual_pairs})
        textual_pairs.sort()
        print(textual_pairs)

        self.face_images = (
            self.__load_face_images(face_images_dir)
            if face_images_dir is not None
            else {}
        )
        unique_entity_names = {
            entity
            for entity in [pair[0] for pair in textual_pairs]
            + [pair[1] for pair in textual_pairs]
        }
        matched_face_images = self.match_face_images(list(unique_entity_names))
        self.__build_multimodal_pairs(textual_pairs, matched_face_images)

    def __build_multimodal_pairs(
        self, pairs: list[tuple[str, str]], matched_face_images: dict[str, list]
    ):
        """
        Build pairs of entities from the knowledge graph
        and their corresponding face images.

        Parameters:
        ----------
        pairs : the pairs of entities
        matched_face_images : the face images of the entities
        """

        self.texts1 = []
        self.texts2 = []
        self.images1 = []
        self.images2 = []
        for head, tail in pairs:
            self.texts1.append(head)
            self.texts2.append(tail)
            head_image, tail_image = (
                matched_face_images.get(head, [None])[0],
                matched_face_images.get(tail, [None])[0],
            )
            self.images1.append(head_image)
            self.images2.append(tail_image)

        self.text_text_pairs = list(zip(self.texts1, self.texts2))
        self.text_image_pairs = [
            (text, image)
            for text, image in zip(self.texts1, self.images2)
            if image is not None
        ]
        self.image_text_pairs = [
            (image, text)
            for image, text in zip(self.images1, self.texts2)
            if image is not None
        ]
        self.image_image_pairs = [
            pair for pair in zip(self.images1, self.images2) if None not in pair
        ]
        self.labels = [1] * (
            len(self.text_text_pairs)
            + len(self.text_image_pairs)
            + len(self.image_text_pairs)
            + len(self.image_image_pairs)
        )

    def match_face_images(self, names: list[str]) -> dict[str, list]:
        """
        Match the face images to the names of the characters
        using fuzzy string matching.
        If a name is not matched, then the name will not be included
        in the returned dictionary.

        Parameters:
        ----------
        names : the names of the characters

        Returns:
        -------
        images : a dictionary mapping the name of the character to a list of face images
        """
        canonical_names = list(self.face_images.keys())
        images = {}
        for name in names:
            match = fuzz_process.extractOne(name, canonical_names, score_cutoff=90)
            if match is not None:
                match, _ = match
                images[name] = self.face_images[match]
        return images

    def __load_face_images(self, path: str):
        """
        Load the face images from a directory.

        Parameters:
        ----------
        path : the path to the directory containing the face images, where
                the name of each subdirectory is the name of the character.

        Returns:
        -------
        images : a dictionary mapping the name of the character to a list of face images
        """

        images: dict[str, list] = {}
        for sub_dir in os.listdir(path):
            if not os.path.isdir(os.path.join(path, sub_dir)):
                continue
            for image_path in os.listdir(os.path.join(path, sub_dir)):
                image = process_image(os.path.join(path, sub_dir, image_path))
                if image is not None:
                    name = sub_dir
                    if name not in images:
                        images[name] = []
                    images[name].append(image)
        return images

    def __load_knowledge_graph_with_embeddings(self, path: str):
        """
        Load th knowledge grpah from a CSV with columns (head, relation, relation_embedding, tail).

        Parameters:
        ----------
        path : the path to the CSV file

        Returns:
        -------
        df : the knowledge graph as a pandas dataframe
        """

        df = pd.read_csv(path)
        df["relation_embedding"] = df["relation_embedding"].apply(json.loads)
        return df

    def __kg_df_to_graph(self, kg_df):
        """
        Convert a knowledge graph to a networkx graph.

        Parameters:
        ----------
        kg_df : the knowledge graph as a pandas dataframe

        Returns:
        -------
        graph : the knowledge graph as a networkx graph
        """
        graph = nx.from_pandas_edgelist(
            kg_df,
            "head",
            "tail",
            ["relation", "relation_embedding"],
            create_using=nx.MultiDiGraph(),
        )
        return graph

    def __build_pairs(self, kg: nx.Graph, num_hops: int = 3):
        """
        Build pairs of entities from the knowledge graph
        based on symmetric relations. For example, (Tom, plays, football)
        and (Erica, plays, football) will result in the pair (Tom, Erica).
        In order to account for more fuzzy relation matching, we will use
        the cosine similarity between the relation embeddings to determine
        if two relations are similar enough to be considered symmetric.

        Parameters:
        ----------
        kg : the knowledge graph
        num_hops : the number of hops to consider for symmetric relations

        Yields:
        ------
        pairs : tuples of entities that are symmetrically related
        """
        kg = nx.reverse_view(kg)
        for pivot in kg.nodes:
            pairs = {0: [(pivot, pivot)]}
            for k in range(1, num_hops + 1):
                if k not in pairs:
                    pairs[k] = []
                for u, v in pairs[k - 1]:
                    for ux, uy in itertools.product(
                        kg.edges(u, data=True), kg.edges(v, data=True)
                    ):
                        _, x, ux_data = ux
                        _, y, vy_data = uy
                        if x == y:
                            continue
                        if (x, y) in pairs[k - 1]:
                            continue
                        if (
                            self.__relation_similarity(
                                ux_data["relation_embedding"],
                                vy_data["relation_embedding"],
                            )
                            >= 0.90
                        ):
                            pairs[k].append((x, y))
                            yield (x, y)

    def __relation_similarity(self, relation1, relation2):
        """
        Calculate the cosine similarity between two relation embeddings.

        Parameters:
        ----------
        relation1 : the first relation embedding
        relation2 : the second relation embedding

        Returns:
        -------
        similarity : the cosine similarity between the two relation embeddings
        """
        return np.dot(relation1, relation2) / (
            np.linalg.norm(relation1) * np.linalg.norm(relation2)
        )

    def __len__(self):
        return (
            len(self.text_text_pairs)
            + len(self.text_image_pairs)
            + len(self.image_text_pairs)
            + len(self.image_image_pairs)
        )

    def __getitem__(self, idx):
        if idx < len(self.text_text_pairs):
            return {
                "text1": self.text_text_pairs[idx][0],
                "text2": self.text_text_pairs[idx][1],
                "label": self.labels[idx],
            }
        if idx < len(self.text_text_pairs) + len(self.text_image_pairs):
            idx = idx - len(self.text_text_pairs)
            return {
                "text1": self.text_image_pairs[idx][0],
                "image2": self.text_image_pairs[idx][1],
                "label": self.labels[idx],
            }
        if idx < (
            len(self.text_text_pairs)
            + len(self.text_image_pairs)
            + len(self.image_text_pairs)
        ):
            idx = idx - (len(self.text_text_pairs) + len(self.text_image_pairs))
            return {
                "image1": self.image_text_pairs[idx][0],
                "text2": self.image_text_pairs[idx][1],
                "label": self.labels[idx],
            }
        idx = idx - (
            len(self.text_text_pairs)
            + len(self.text_image_pairs)
            + len(self.image_text_pairs)
        )
        return {
            "image1": self.image_image_pairs[idx][0],
            "image2": self.image_image_pairs[idx][1],
            "label": self.labels[idx],
        }

class ContrastiveLoss(nn.Module):
    """
    Vanilla Contrastive loss, also called InfoNceLoss as in SimCLR paper

    [Source](https://theaisummer.com/simclr/)
    """

    def __init__(self, batch_size, temperature=0.5):
        super().__init__()
        self.batch_size = batch_size
        self.temperature = temperature

    def __mask(self, batch_size):
        mask = (~torch.eye(batch_size * 2, batch_size * 2, dtype=bool)).float()
        return mask

    def calc_similarity_batch(self, a, b):
        representations = torch.cat([a, b], dim=0)
        return torch.cosine_similarity(
            representations.unsqueeze(1), representations.unsqueeze(0), dim=2
        )

    def forward(self, proj_1, proj_2):
        """
        proj_1 and proj_2 are batched embeddings [batch, embedding_dim]
        where corresponding indices are pairs
        z_i, z_j in the SimCLR paper
        """
        batch_size = proj_1.shape[0]
        z_i = torch.nn.functional.normalize(proj_1, p=2, dim=1)
        z_j = torch.nn.functional.normalize(proj_2, p=2, dim=1)

        similarity_matrix = self.calc_similarity_batch(z_i, z_j)

        sim_ij = torch.diag(similarity_matrix, batch_size)
        sim_ji = torch.diag(similarity_matrix, -batch_size)

        positives = torch.cat([sim_ij, sim_ji], dim=0)

        nominator = torch.exp(positives / self.temperature)

        denominator = self.__mask(batch_size).to(similarity_matrix.device) * torch.exp(
            similarity_matrix / self.temperature
        )

        all_losses = -torch.log(nominator / torch.sum(denominator, dim=1))
        loss = torch.sum(all_losses) / (2 * self.batch_size)
        return loss

class MultiModalKGCLIP(nn.Module):
    def __init__(self, batch_size=128, num_epochs=40, learning_rate=1e-5):
        super().__init__()
        (
            self.text_model,
            self.text_processor,
            self.image_model,
            self.image_processor,
        ) = self.__get_models()
        self.batch_size = batch_size
        self.num_epochs = num_epochs
        self.learning_rate = learning_rate

    @staticmethod
    def __get_models():
        model_name = "openai/clip-vit-base-patch16"
        device = torch.device("cuda" if torch.cuda.is_available() else "cpu")

        # Load the CLIP model and processor
        text_model = CLIPTextModelWithProjection.from_pretrained(model_name).to(device)
        text_processor = AutoTokenizer.from_pretrained(model_name)

        image_model = CLIPVisionModelWithProjection.from_pretrained(model_name).to(
            device
        )
        image_processor = AutoProcessor.from_pretrained(model_name).image_processor
        return (
            text_model,
            text_processor,
            image_model,
            image_processor,
        )

    def forward(self, text_input, image_input):
        text_embedding = self.text_model(**text_input).text_embeds
        image_embedding = self.image_model(**image_input).image_embeds
        return text_embedding, image_embedding

    def save_pretrained(self, output_dir):
        self.text_model.save_pretrained(os.path.join(output_dir, "text_model"))
        self.image_model.save_pretrained(os.path.join(output_dir, "image_model"))

    @classmethod
    def from_pretrained(cls, pretrained_dir):
        text_model = CLIPTextModelWithProjection.from_pretrained(
            os.path.join(pretrained_dir, "text_model")
        )
        image_model = CLIPVisionModelWithProjection.from_pretrained(
            os.path.join(pretrained_dir, "image_model")
        )
        return cls(text_model, image_model)

    def train(self, mode: bool = True):
        self.text_model.train(mode)
        self.image_model.train(mode)
        return super().train(mode)

    def eval(self):
        self.text_model.eval()
        self.image_model.eval()
        return super().eval()

    def __get_batch_type(self, batch):
        if "text1" in batch and "text2" in batch:
            return "text_text"
        if "text1" in batch and "image2" in batch:
            return "text_image"
        if "image1" in batch and "text2" in batch:
            return "image_text"
        if "image1" in batch and "image2" in batch:
            return "image_image"
        raise ValueError("Invalid batch")

    def fit(self, data_loader: DataLoader):
        criterion = ContrastiveLoss(self.batch_size)
        text_optimizer = optim.AdamW(
            self.text_model.parameters(), lr=self.learning_rate
        )
        image_optimizer = optim.AdamW(
            self.image_model.parameters(), lr=self.learning_rate
        )
        # Fine-tuning loop
        self.train()
        for epoch in range(self.num_epochs):
            total_loss = 0.0
            for batch in data_loader:
                batch_type = self.__get_batch_type(batch)
                if batch_type == "text_text":
                    text_optimizer.zero_grad()

                    text_input_1 = self.text_processor(
                        batch["text1"], return_tensors="pt", padding=True
                    )
                    text_input_2 = self.text_processor(
                        batch["text2"], return_tensors="pt", padding=True
                    )
                    text_input_1, text_input_2 = (
                        text_input_1.to(self.text_model.device),
                        text_input_2.to(self.text_model.device),
                    )
                    loss = criterion(
                        self.text_model(**text_input_1).text_embeds,
                        self.text_model(**text_input_2).text_embeds,
                    )
                    loss.backward()
                    text_optimizer.step()
                elif batch_type == "text_image":
                    text_optimizer.zero_grad()
                    image_optimizer.zero_grad()

                    text_input = self.text_processor(
                        batch["text1"], return_tensors="pt", padding=True
                    )
                    image_input = self.image_processor(
                        batch["image2"], return_tensors="pt", padding=True
                    )
                    text_input, image_input = (
                        text_input.to(self.text_model.device),
                        image_input.to(self.image_model.device),
                    )
                    loss = criterion(
                        self.text_model(**text_input).text_embeds,
                        self.image_model(**image_input).image_embeds,
                    )
                    loss.backward()
                    text_optimizer.step()
                    image_optimizer.step()
                elif batch_type == "image_text":
                    text_optimizer.zero_grad()
                    image_optimizer.zero_grad()

                    image_input = self.image_processor(
                        batch["image1"], return_tensors="pt", padding=True
                    )
                    text_input = self.text_processor(
                        batch["text2"], return_tensors="pt", padding=True
                    )
                    image_input, text_input = (
                        image_input.to(self.image_model.device),
                        text_input.to(self.text_model.device),
                    )
                    loss = criterion(
                        self.image_model(**image_input).image_embeds,
                        self.text_model(**text_input).text_embeds,
                    )
                    loss.backward()
                    text_optimizer.step()
                    image_optimizer.step()
                elif batch_type == "image_image":
                    image_optimizer.zero_grad()

                    image_input_1 = self.image_processor(
                        batch["image1"], return_tensors="pt", padding=True
                    )
                    image_input_2 = self.image_processor(
                        batch["image2"], return_tensors="pt", padding=True
                    )
                    image_input_1, image_input_2 = (
                        image_input_1.to(self.image_model.device),
                        image_input_2.to(self.image_model.device),
                    )
                    loss = criterion(
                        self.image_model(**image_input_1).image_embeds,
                        self.image_model(**image_input_2).image_embeds,
                    )
                    loss.backward()
                    image_optimizer.step()

                total_loss += loss.item()

            avg_loss = total_loss / len(data_loader)
            print(f"Epoch {epoch + 1}/{self.num_epochs} - Average Loss: {avg_loss}")

    def predict_text(self, text_data):
        self.eval()
        with torch.no_grad():
            # get the text embedding
            text_input = self.text_processor(
                text_data, return_tensors="pt", padding=True
            )
            text_input = text_input.to(self.text_model.device)
            text_embedding = self.text_model(**text_input).text_embeds
            return text_embedding

    def predict_image(self, image_data):
        self.eval()
        with torch.no_grad():
            # get the image embedding
            image_input = self.image_processor(
                image_data, return_tensors="pt", padding=True
            )
            image_input = image_input.to(self.image_model.device)
            image_embedding = self.image_model(**image_input).image_embeds
            return image_embedding

    def evaluate(
        self, text_data_1=None, text_data_2=None, image_data_1=None, image_data_2=None
    ):
        if (
            sum(
                [
                    1 if text_data_1 is not None else 0,
                    1 if text_data_2 is not None else 0,
                    1 if image_data_1 is not None else 0,
                    1 if image_data_2 is not None else 0,
                ]
            )
            != 2
        ):
            raise ValueError(
                "Exactly two of text_data_1, text_data_2, image_data_1, image_data_2 must be provided"
            )
        if text_data_1 is not None and text_data_2 is not None:
            data_1 = text_data_1
            data_2 = text_data_2
            labels_1 = data_1
            labels_2 = data_2
            kind_1 = "text1"
            kind_2 = "text2"
            predict1 = self.predict_text
            predict2 = self.predict_text
        elif text_data_1 is not None and image_data_2 is not None:
            data_1 = text_data_1
            data_2 = list(list(zip(*image_data_2))[1])
            labels_1 = data_1
            labels_2 = list(list(zip(*image_data_2))[0])
            kind_1 = "text1"
            kind_2 = "image2"
            predict1 = self.predict_text
            predict2 = self.predict_image
        elif image_data_1 is not None and text_data_2 is not None:
            data_1 = list(list(zip(*image_data_1))[1])
            data_2 = text_data_2
            labels_1 = list(list(zip(*image_data_1))[0])
            labels_2 = data_2
            kind_1 = "image1"
            kind_2 = "text2"
            predict1 = self.predict_image
            predict2 = self.predict_text
        elif image_data_1 is not None and image_data_2 is not None:
            data_1 = list(list(zip(*image_data_1))[1])
            data_2 = list(list(zip(*image_data_2))[1])
            labels_1 = list(list(zip(*image_data_1))[0])
            labels_2 = list(list(zip(*image_data_2))[0])
            kind_1 = "image1"
            kind_2 = "image2"
            predict1 = self.predict_image
            predict2 = self.predict_image
        self.eval()
        with torch.no_grad():
            # calculate the cosine similarity between the two embeddings
            embedding_1 = predict1(data_1)
            embedding_2 = predict2(data_2)
            similarity_scores = nn.CosineSimilarity(dim=1)(embedding_1, embedding_2)
            print(
                pd.DataFrame.from_dict(
                    {
                        kind_1: labels_1,
                        kind_2: labels_2,
                        "similarity": similarity_scores.tolist(),
                    }
                )
            )

def plot_text_embeddings(model, text_data, save_to: Optional[str] = None):
    tsne = TSNE(n_components=2, random_state=0, metric="cosine", perplexity=5)
    visualisation_set = text_data
    text_embeddings = model.predict_text(visualisation_set).cpu().numpy()
    text_embeddings = tsne.fit_transform(text_embeddings)
    text_embeddings = pd.DataFrame(
        np.hstack((text_embeddings, np.array(visualisation_set).reshape(-1, 1))),
        columns=["x", "y", "text"],
    )
    text_embeddings["x"] = text_embeddings["x"].astype(float)
    text_embeddings["y"] = text_embeddings["y"].astype(float)
    fig = px.scatter(text_embeddings, x="x", y="y", text="text")
    fig.update_traces(textposition="top center")
    fig.update_layout(
        height=800,
        title_x=0.5,
        title_y=0.9,
        title_font_size=30,
        font=dict(size=18),
    )
    fig.update_xaxes(
        range=[text_embeddings["x"].min() - 50, text_embeddings["x"].max() + 50]
    )
    fig.update_yaxes(
        range=[text_embeddings["y"].min() - 5, text_embeddings["y"].max() + 5]
    )
    if save_to is not None:
        fig.write_image(save_to)
    fig.show()

def plot_image_embeddings(
    model, image_data: list[tuple[str, Image]], save_to: Optional[str] = None
):
    tsne = TSNE(
        n_components=2,
        random_state=0,
        metric="cosine",
        perplexity=min(5, len(image_data) - 1),
    )
    names, visualisation_set = list(zip(*image_data))
    names = list(names)
    visualisation_set = list(visualisation_set)
    image_embeddings = model.predict_image(visualisation_set).cpu().numpy()
    image_embeddings = tsne.fit_transform(image_embeddings)
    image_embeddings = pd.DataFrame(
        np.hstack((image_embeddings, np.array(names).reshape(-1, 1))),
        columns=["x", "y", "image"],
    )
    image_embeddings["x"] = image_embeddings["x"].astype(float)
    image_embeddings["y"] = image_embeddings["y"].astype(float)
    fig = px.scatter(image_embeddings, x="x", y="y", text="image")
    fig.update_traces(textposition="top center")
    fig.update_layout(
        height=800,
        title_x=0.5,
        title_y=0.9,
        title_font_size=30,
        font=dict(size=18),
    )
    fig.update_xaxes(
        range=[image_embeddings["x"].min() - 50, image_embeddings["x"].max() + 50]
    )
    fig.update_yaxes(
        range=[image_embeddings["y"].min() - 5, image_embeddings["y"].max() + 5]
    )
    if save_to is not None:
        fig.write_image(save_to)
    fig.show()

def pairwise_distances(
    X, metric: Literal["cosine", "euclidean"] = "euclidean", **kwargs
):
    """
    Compute the pairwise distance between X and Y.

    Parameters
    ----------
    X : array-like of shape (n_samples, n_features)

    metric : either "cosine" or "euclidean"

    Returns
    -------
    distances : ndarray of shape (n_samples_X, n_samples_Y)
    """
    if metric == "cosine":
        X_normalized = X / np.linalg.norm(X, axis=1)[:, np.newaxis]
        return np.dot(X_normalized, X_normalized.T)
    elif metric == "euclidean":
        return np.sqrt(
            np.sum(X**2, axis=1)[:, np.newaxis]
            + np.sum(X**2, axis=1)[np.newaxis, :]
            - 2 * np.dot(X, X.T)
        )
    raise ValueError(f"Invalid metric '{metric}'")

def representation_dist_matrix(
    model: "MultiModalKGCLIP", text_data=None, image_data=None
):
    if text_data is None and image_data is None:
        raise ValueError("Either text_data or image_data must be provided")
    if text_data is not None and image_data is not None:
        raise ValueError("Only one of text_data or image_data must be provided")
    if text_data is not None:
        data = text_data
        predict = model.predict_text
    else:
        _, visualisation_set = list(zip(*image_data))
        visualisation_set = list(visualisation_set)
        data = visualisation_set
        predict = model.predict_image
    embeddings = predict(data).cpu().numpy()
    return pd.DataFrame(
        pairwise_distances(embeddings, metric="cosine"),
        columns=data,
        index=data,
    )

def representation_dist_matrix_correlation(X, Y) -> tuple[float, float]:
    """
    Compute the correlation between the pairwise distance matrices of X and Y.

    Parameters
    ----------
    X : array-like of shape (n_samples, n_features)
    Y : array-like of shape (n_samples, n_features)

    Returns
    -------
    correlation : float
    p_value : float
    """
    return distance_corr(
        pairwise_distances(X, metric="cosine").ravel(),
        pairwise_distances(Y, metric="cosine").ravel(),
    )

def tuple_uniq_by_index(tuples: list[tuple], index: int) -> list[tuple]:
    """
    Remove duplicates from a list of tuples based on a given index.

    Parameters
    ----------
    tuples : the list of tuples
    index : the index to use for comparison

    Returns
    -------
    unique_tuples : the list of tuples with duplicates removed
    """
    seen = set()
    unique_tuples = []
    for tup in tuples:
        if tup[index] not in seen:
            seen.add(tup[index])
            unique_tuples.append(tup)
    return unique_tuples

if __name__ == "__main__":
    batch_size = 128

    model = MultiModalKGCLIP(batch_size=batch_size, num_epochs=40)

    eval_1 = [
        "Sam",
        "Solomon",
        "Anne",
        "Anne",
        "Uncle Abram",
        "Solomon",
        "Solomon",
        "Edwin Epps",
        "William Ford",
        "Merrill Brown",
        "Judge Turner",
        "Solomon",
        "William Ford",
        "A picture of an apple",
        "A glass of water on the table",
    ]
    eval_2 = [
        "Solomon",
        "Anne",
        "Alonzo",
        "Margaret",
        "Alonzo",
        "Slaves",
        "Free man",
        "Solomon",
        "Solomon",
        "Abram Hamilton",
        "Solomon",
        "Clemens Ray",
        "Edwin Epps",
        "A picture of an orange",
        "An airplane in the sky",
    ]

    dataset = KGDataset(
        "../results/12_years_a_slave.csv",
        "/Users/nate/Downloads/clustered-finetuned/test",
        max_pairs=1000,
    )

    text_eval_entities = [
        "Solomon Northup",
        "Patsey",
        "Tibeats",
        "Armsby",
        "Mrs. Epps",
        "Mistress Shaw",
        "William Ford",
        "Judge Turner",
        "Master Shaw",
        "Edwin Epps",
        "Abram Hamilton",
        "Eliza",
        "Clemens Ray",
        "Anne Northup",
        "Alonzo Northup",
        "Margaret Northup",
        "Sam",
        "Uncle Abram",
        "Free man",
        "Bass",
    ]

    image_eval_entities = [
        (name, images[0])
        for name, images in dataset.match_face_images(text_eval_entities).items()
    ]

    matching_images = dataset.match_face_images(list(set(eval_1 + eval_2)))

    image_eval_1 = [
        (name, matching_images[name][0])
        for i, name in enumerate(eval_1)
        if name in matching_images and eval_2[i] in matching_images
    ]
    image_eval_2 = [
        (name, matching_images[name][0])
        for i, name in enumerate(eval_2)
        if name in matching_images and eval_1[i] in matching_images
    ]

    print("Pre-training:")
    model.evaluate(eval_1, eval_2)
    model.evaluate(image_data_1=image_eval_1, image_data_2=image_eval_2)
    plot_text_embeddings(
        model, list(set(eval_1 + eval_2)), save_to="pre_embeddings_text.svg"
    )
    plot_image_embeddings(
        model,
        tuple_uniq_by_index(image_eval_1 + image_eval_2, 0),
        save_to="pre_embeddings_image.svg",
    )

    pre_train_text_embeddings = model.predict_text(text_eval_entities).cpu().numpy()
    pre_train_text_embeddings_have_images = (
        model.predict_text(
            [
                entity
                for entity in text_eval_entities
                if entity in set(list(zip(*image_eval_entities))[0])
            ]
        )
        .cpu()
        .numpy()
    )
    pre_train_image_embeddings = (
        model.predict_image([image for _, image in image_eval_entities]).cpu().numpy()
    )

    model.fit(
        DataLoader(
            dataset,
            batch_sampler=KGDatasetBatchSampler(dataset, batch_size),
        )
    )
    output_dir = "./fine_tuned_clip_model"
    model.save_pretrained(output_dir)
    print(f"Saved model to '{output_dir}'")
    try:
        torch.save(model, os.path.join(output_dir, "model.pt"))
    except Exception as e:
        print("Failed to save model: ", e)
    print("Post-training:")
    post_train_text_embeddings = model.predict_text(text_eval_entities).cpu().numpy()
    post_train_text_embeddings_have_images = (
        model.predict_text(
            [
                entity
                for entity in text_eval_entities
                if entity in set(list(zip(*image_eval_entities))[0])
            ]
        )
        .cpu()
        .numpy()
    )
    post_train_image_embeddings = (
        model.predict_image([image for _, image in image_eval_entities]).cpu().numpy()
    )

    pre_train_image_text_corr, pre_p_val = distance_corr(
        pre_train_text_embeddings_have_images, pre_train_image_embeddings
    )
    post_train_image_text_corr, post_p_val = distance_corr(
        post_train_text_embeddings_have_images, post_train_image_embeddings
    )
    print(
        f"Pre-training image-text correlation: {pre_train_image_text_corr:.3f} (p<{pre_p_val:.3f}; n={len(post_train_text_embeddings_have_images)}), Post-training image-text correlation: {post_train_image_text_corr:.3f} (p<{post_p_val:.3f}; n={len(post_train_text_embeddings_have_images)})"
    )
    pre_post_image_corr, image_p_val = distance_corr(
        pre_train_image_embeddings, post_train_image_embeddings
    )
    pre_post_text_corr, text_p_val = distance_corr(
        pre_train_text_embeddings, post_train_text_embeddings
    )
    print(
        f"Pre-post-training image-image correlation: {pre_post_image_corr:.3f} (p<{image_p_val:.3f}; n={len(pre_train_text_embeddings)}), Pre-post-training text-text correlation: {pre_post_text_corr:.3f} (p<{text_p_val:.3f}; n={len(pre_train_text_embeddings)})"
    )
    pre_image_post_text_corr, pre_image_post_text_p_val = distance_corr(
        pre_train_image_embeddings, post_train_text_embeddings_have_images
    )
    pre_text_post_image_corr, pre_text_post_image_p_val = distance_corr(
        pre_train_text_embeddings_have_images, post_train_image_embeddings
    )
    print(
        f"Pre-training image-post-training text correlation: {pre_image_post_text_corr:.3f} (p<{pre_image_post_text_p_val:.3f}; n={len(post_train_image_embeddings)}), Pre-training text-post-training image correlation: {pre_text_post_image_corr:.3f} (p<{pre_text_post_image_p_val:.3f}; n={len(post_train_image_embeddings)})"
    )
    model.evaluate(eval_1, eval_2)
    model.evaluate(image_data_1=image_eval_1, image_data_2=image_eval_2)
    plot_text_embeddings(
        model, list(set(eval_1 + eval_2)), save_to="post_embeddings_text.svg"
    )
    plot_image_embeddings(
        model,
        tuple_uniq_by_index(image_eval_1 + image_eval_2, 0),
        save_to="post_embeddings_image.svg",
    )