import pyttsx3
import speech_recognition as sr
import datetime
import wikipedia
import webbrowser

print("READ THIS")
print("WHAT YOUR JARVIS CAN DO")
print("Your Jarvis can search in chrome\n can summarize wikipedia\n can open google and youtube\n  can show you IPL score\n can open National Geographic\n can recommend the best educational channel \n can open the famous coding YT Channel Code With Harry\n can show you recommended Hindi news channel\n Can plat latest music playlist having more than 1000 songs from T Series\n can show you recommended marathi news channel\n can open Adobe Reader 9\n can open amazon\n can also open recommended coding website of stack overflow\n can also tell present time\n can also open SonyLiv website")
print("Jarvis can also answer your questions as\n How are you?\n Who are you?\n How do you do?\n What are you doing?\n What is your name?\n Are you a robot? ")
print("Your Jarvis can also reply on your comments like\n Really!\n Hello\n I am fine\n")
print("Your Jarvis can quit by saying\n Quit \n Bye \n Goodbye \n Exit \n \n \n")
engine = pyttsx3.init('sapi5')
voices = engine.getProperty('voices')
engine.setProperty('voice', voices[0].id)

print("Enter your Name:")
username = str(input())

def speak(audio):
    engine.say(audio)
    engine.runAndWait()

def wishMe():
    hour = int(datetime.datetime.now().hour)
    if hour>=0 and hour<12:
        speak('Good Morning Sir')
    elif hour>=12 and hour<18:
        speak('Good Afternoon sir')
    else:
        speak('Good Evening sir')

    speak('I am Jarvis. How can I help you?')
def takeCommand():
    r = sr.Recognizer()
    with sr.Microphone() as source:
        print('Listening...')
        r.pause_threshold = 1
        audio = r.listen(source)

    try:
        print('Recognizing...')
        query = r.recognize_google(audio, language='en')
        print(f"User said: {query}\n")

    except Exception as e:

        print("say that again pls sir")
        return "None"
    return query

if __name__ == "__main__":
   wishMe()
   while True:
        query = takeCommand().lower()
   #executing task based on query

        if'wikipedia' in query:
            speak('Searching wikipedia')
            query = query.replace("wikipedia", "")
            results = wikipedia.summary(query, sentences=2)
            speak("According to Wikipedia")
            print(results)
            speak(results)
        elif'youtube' in query:
            webbrowser.open("www.youtube.com")
            speak("This is the official website of YouTube")
        elif'google' in query:
            webbrowser.open("www.google.com")
            speak("This is the official website of Google.")
        elif'how are you' in query:
            speak("I am fine sir")
            print("I am fine sir")
        elif'what are you doing' in query:
            speak("I am spending time with you")
            print("I am spending time with you")
        elif'your name' in query:
            speak("My name is Jarvis sir")
            print("My name is Jarvis, sir")
        elif'who are you' in query:
            speak("I am Jarvis sir")
            print("I am Jarvis sir")
        elif'well done' in query:
            speak("Thank you its my pleasure")
            print("Thank you its my pleasure")
        elif'thank you' in query:
            speak("You are welcome sir")
            print("You are welcome sir")
        elif'sports' in query:
            webbrowser.open("https://news.google.com/topics/CAAqJggKIiBDQkFTRWdvSUwyMHZNRFp1ZEdvU0FtVnVHZ0pWVXlnQVAB?hl=en-IN&gl=IN&ceid=IN:en")
            speak("These are sports news from google news.")
        elif'exit' in query:
            speak(f"Goodbye {username}, Take care")
            print(f"Goodbye {username}, Take care")
            exit()
        elif'bye' in query:
            speak("Bye sir see you next time")
            exit()
        elif'goodbye' in query:
            speak("Goodbye sir, see you next time")
            exit()
        elif'music'in query:
            webbrowser.open("https://www.youtube.com/aashiqui2/videos")
            speak("These are the music from T-Series YouTube Channel")
        elif'really' in query:
            speak("yeah sir")
        elif'are you robot' in query:
            speak("yes sir, I am Jarvis, a desktop assistant.")
            print("yes sir, I am Jarvis, a desktop assistant.")
        elif'scientific hindi' in query:
            webbrowser.open("https://www.youtube.com/c/FactTechz/videos")
            speak("This is the most famous science fiction YouTube channel in hindi named Facttechz")
        elif'hindi science' in query:
            webbrowser.open("https://www.youtube.com/c/FactTechz/videos")
        elif'hindi scientific' in query:
            webbrowser.open("https://www.youtube.com/c/FactTechz/videos")
            speak("This is the most famous science fiction YouTube channel in hindi named Facttechz")
        elif'play latest music' in query:
            webbrowser.open("https://www.youtube.com/watch?v=b8--JS9lRnI&list=PL095CA373D64B70C0")
            speak("This is the playlist of latest song by T series")
        elif'hello' in query:
            speak(f"hello {username}")
            print(f"hello {username}")
        elif'quit' in query:
            speak("Bye")
            print("Bye")
            exit()
        elif'scientific channel' in query:
            webbrowser.open("https://www.youtube.com/c/NatGeo/videos")
            speak("This is the best scientific YouTube channel names National Geographic")
        elif'codewithharry' in query:
            webbrowser.open("https://www.youtube.com/c/CodeWithHarry/videos")
            speak("This is the best coding YouTube channel named Code With Harry")
        elif'best educational channel' in query:
            webbrowser.open("https://www.youtube.com/c/DearSir/videos")
            speak("This is the most best educational youtube channel, who teaches English and Mathematics for seondary, high secondary and students who are preparing themselves for competitive exams and also teaches us English speaking and short tricks for Maths. This channel is named as Dear Sir")
        elif'time' in query:
            strTime = datetime.datetime.now().strftime("%H:%M:%S")
            speak(f"Sir, the time is{strTime}")
        elif'stack overflow' in query:
            webbrowser.open("www.stackoverflow.com")
            speak("This is one of the recommended website named stack overflow which teaches you coding")
        elif'how do you do' in query:
            speak("I am fine sir, what about you")
            print("I am fine sir, what about you?")
        elif'am fine' in query:
            speak("Nice to hear that from you sir")
            print("Nice to hear that from you sir")
        elif'hindi news' in query:
            webbrowser.open("https://www.youtube.com/watch?v=SmQqAnKG6zs")
            speak("This is the live news from hindi")
        elif'live news' in query:
            webbrowser.open("https://www.youtube.com/watch?v=9Auq9mYxFEE")
            speak("This is the live news from Sky News in English")
        elif'marathi news' in query:
            webbrowser.open("https://www.youtube.com/watch?v=itJLgnqZ3U8")
            speak("This is the live news from ABP maza in marathi")
        elif'amazon' in query:
            webbrowser.open("https://www.amazon.in/?ext_vrnc=hi&tag=googhydrabk-21&ascsubtag=_k_EAIaIQobChMIipy3m8iB7QIVBpZLBR3Lqwg6EAAYASAAEgJNlfD_BwE_k_&ext_vrnc=hi&gclid=EAIaIQobChMIipy3m8iB7QIVBpZLBR3Lqwg6EAAYASAAEgJNlfD_BwE")
            speak("This the shopping website named amazon")
        elif'shopping' in query:
            webbrowser.open("https://www.amazon.in/?ext_vrnc=hi&tag=googhydrabk-21&ascsubtag=_k_EAIaIQobChMIipy3m8iB7QIVBpZLBR3Lqwg6EAAYASAAEgJNlfD_BwE_k_&ext_vrnc=hi&gclid=EAIaIQobChMIipy3m8iB7QIVBpZLBR3Lqwg6EAAYASAAEgJNlfD_BwE")
            speak("This the shopping website named amazon")
        elif'sonyliv' in query:
            webbrowser.open("https://www.sonyliv.com/")
            speak("This is the sony leave website. You can use it for watching TV shows")
        elif'best entertaining' in query:
            webbrowser.open("https://www.sonyliv.com/")
            speak("This is the sony leave website. You can use it for watching TV shows")
        elif'entertainment' in query:
            webbrowser.open("https://www.sonyliv.com/")
            speak("This is the sonyliv website. You can use it for watching TV shows")
        elif'national geographic'in query:
            webbrowser.open("https://www.youtube.com/c/NatGeo/videos")
            speak("This is the best scientific YouTube channel names National Geographic")
        elif'hindi news' in query:
            webbrowser.open("https://www.youtube.com/watch?v=SmQqAnKG6zs")
            speak("This is the live news from hindi")
        elif'gmail' in query:
            webbrowser.open("https://mail.google.com/mail/u/0/#inbox")
            speak("Starting Google Mail")
        elif'meet' in query:
            webbrowser.open("https://meet.google.com/")
            speak("Starting Google Meet")


import random
import json

import numpy as np
import torch
from torch.utils.data import Dataset, DataLoader, random_split, ConcatDataset
import pandas as pd
import os

import torchaudio

from dataset import snoring_preprocess
from dataset import dataset_utils

# TODO: some inspections for arguments
# TODO: ABC? (to a meta dataset)
# TODO: split rate for testing --> train and testing dataset building need to be separated
# TODO: Considering a reusing dataset builder (Can we minimize the building effort?)

class MetaDataset(Dataset):
    def __init__(self, loader, data_refs, dataset_name=None, transform=None, target_loader=None):
        self.loader = loader
        self.data_refs = data_refs
        self.dataset_name = dataset_name
        self.transform = transform
        self.target_loader = target_loader
        self.input_key = 'input'
        self.target_key = 'target'
        self.log_dataset()
        self.dataflow_func = self.get_dataflow_format()

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

    def __getitem__(self, idx):
        return self.dataflow_func(idx)

    def get_dataflow_format(self):
        first_sample = self.data_refs[0]
        if isinstance(first_sample, dict):
            assert self.input_key in first_sample, 'No input data reference'
            if self.target_key in first_sample:
                return self.get_datapair_from_dict_seq
            else:
                return self.get_input_from_dict_seq
        elif isinstance(first_sample, str):
            return self.get_input_from_str_seq
        else:
            raise ValueError('Unknown reference format.')

    def get_input_from_str_seq(self, idx):
        input_data = self.loader(self.data_refs[idx])
        if self.transform is not None:
            input_data = self.transform(input_data)
        return input_data

    def get_input_from_dict_seq(self, idx):
        input_data = self.loader(self.data_refs[idx]['input'])
        if self.transform is not None:
            input_data = self.transform(input_data)
        return input_data

    def get_datapair_from_dict_seq(self, idx):
        input_data = self.loader(self.data_refs[idx]['input'])
        if self.target_loader is not None:
            target = self.target_loader(self.data_refs[idx]['target'])
        else:
            target = self.data_refs[idx]['target']
        if self.transform is not None:
            input_data, target = self.transform(input_data, target)
        return {'input': input_data, 'target': target}

    def log_dataset(self):
        pass

    def get_ref_mapping():
        pass

def wav_loader(filename, sr=None, channels=1):
    y = dataset_utils.get_pydub_sound(
        filename, 'wav', sr, channels)
    sr = y.frame_rate
    waveform = np.array(y.get_array_of_samples(), np.float32)
    return waveform, sr

# def build_single_snoring_dataset(
#     data, data_refs, loader, data_transform=None, dataset_name=None):
#     data_config = {
#         'data': data,
#         'data_refs': data_refs,
#         'loader': loader,
#         'dataset_name': dataset_name,
#         'transform': data_transform,
#         'target_loader': None,
#     }

#     data_config['data_refs'] = train_refs
#     train_dataset = SnoringDataset(**data_config)

#     data_config['data_refs'] = valid_refs
#     data_config['transform'] = None
#     valid_dataset = SnoringDataset(**data_config)
#     return train_dataset, valid_dataset

# def get_snoring_refs(label_path, split_json):
#     local_ref = json.load(split_json)
#     label_table = pd.read_csv(label_path)
#     return path_refs

# XXX: change to JSON format
def get_snoring_refs(data_ref_path, data_root):
    data_refs = pd.read_csv(data_ref_path)
    path_refs = snoring_preprocess.get_path_refs_fast(data_root, data_refs, suffix='wav')
    return path_refs

# XXX: Put in here currently
def snoring_transform(waveform):
    waveform = wavform_transform(waveform)
    spec = wavform_to_spec(waveform)
    spec = spec_transform(spec)
    return spec

# XXX: return sr?
def torchaudio_loader(path):
    waveform, sr = torchaudio.load(path, normalize=True)
    return waveform

def build(data_csv, train_json, valid_json, dataset_name):
    train_data_refs = get_snoring_refs(data_csv, train_json)
    valid_data_refs = get_snoring_refs(data_csv, valid_json)
    loader = torchaudio_loader

    train_dataset = MetaDataset(
        loader, train_data_refs, dataset_name, snoring_transform, target_loader=None)
    valid_dataset = MetaDataset(
        loader, valid_data_refs, dataset_name, snoring_transform, target_loader=None)
    return train_dataset, valid_dataset

def build_dataloader(total_data_config, train_batch_size):
    total_train_dataset = []
    total_valid_dataset = []
    for data_config in total_data_config:
        train_dataset, valid_dataset = build(**data_config)
        total_train_dataset.append(train_dataset)
        total_valid_dataset.append(valid_dataset)

    concat_train_dataset = ConcatDataset(total_train_dataset)
    concat_valid_dataset = ConcatDataset(total_valid_dataset)

    train_loader = DataLoader(concat_train_dataset, train_batch_size, shuffle=True)
    valid_loader = DataLoader(concat_valid_dataset, 1, shuffle=False)
    return train_loader, valid_loader

def main():
    data_root = r'C:\Users\test\Desktop\Leon\Datasets\ASUS_snoring_subset\pp\Samsung_Note10Plus_night\wave_split'
    data_ref_path = r'C:\Users\test\Desktop\Leon\Datasets\ASUS_snoring_subset\pp\Samsung_Note10Plus_night\data.csv'
    data_refs = pd.read_csv(data_ref_path)
    path_refs = snoring_preprocess.get_path_refs_fast(data_root, data_refs, suffix='wav')
    pass
    # build_snoring_dataset(data_roots, data_ref_paths, train_batch_size, data_transform=None)

if __name__ == '__main__':
    main()


import numpy as np
import torch
from torch._C import dtype
import torch.nn as nn
import random
from filters import BP_filter
import matplotlib.pyplot as plt

class Random_shift(nn.Module):
# Randomly shifts the track
    def __init__(self, shift_max):
        super().__init__()
        self.shift_max = shift_max

    def forward(self, noisy_track, clean_track):
        shift = random.randint(0, self.shift_max)
        noisy_track = torch.roll(noisy_track, shift, dims=-1)
        clean_track = torch.roll(clean_track, shift, dims=-1)
        return noisy_track, clean_track

class Remix(nn.Module):
# Randomly shifts the noise in the track
    def forward(self, noisy_track, clean_track):
        noise = noisy_track - clean_track
        perm = torch.randperm(noise.size()[-1])
        noisy_track[-1][-1][:] = clean_track[-1][-1][:] + noise[-1][-1][perm]
        return noisy_track, clean_track

class Band_mask(nn.Module):
# Maskes bands of frequencies
    def __init__(self, sample_rate, min_freq, max_freq):
        super().__init__()
        self.sample_rate = sample_rate
        self.filter = BP_filter(min_freq, max_freq)

    def forward(self, noisy_track, clean_track):
        noisy_track = self.filter.forward(noisy_track)
        clean_track = self.filter.forward(clean_track)
        return noisy_track, clean_track

def test():
    x = torch.randn(1,1,1000)
    y = torch.randn(1,1,1000)
    t = np.linspace(0,1000, num=1000, dtype=np.float32)

    shift = Random_shift(10)
    remix = Remix()
    band_mask = Band_mask(48000, 0.1, 0.15)
    shifted_x, shifted_y = shift.forward(x, y)
    remixed_x, remixed_y = remix.forward(x, y)
    masked_x, masked_y = band_mask.forward(x, y)

    plt.figure()
    plt.title('x')
    plt.plot(t, x[-1][-1])
    plt.figure()
    plt.title('shifted x')
    plt.plot(t, shifted_x[-1][-1])
    plt.figure()
    plt.title('remixed x')
    plt.plot(t, remixed_x[-1][-1])
    plt.figure()
    plt.title('masked x')
    plt.plot(t, masked_x[-1][-1])
    plt.show()

if __name__ == '__main__':
    test()


import socket
from time import sleep

# Keyence Scanner ASCII Commands ------------------------------------------------------

"""
Error Format: ER,COMMAND,ERROR_CODE

Error Codes:
------------
00  Undefined command received
01  Mismatched command format (Invalid number of parameters)
02  The parameter 1 value exceeds the set value
03  The parameter 2 value exceeds the set value
04  Parameter 2 is not set in HEX (hexadecimal) code
05  Parameter 2 set in HEX (hexadecimal) code but exceeds the set value
10  There are 2 or more ! marks in the preset data, Preset data is incorrect
11  Area specification data is incorrect
12  Specified file does not exist
13  "mm" for the %Tmm-LON,bb command exceeds the setting range.
14  Communication cannot be checked with the %Tmm-KEYENCE command.
20  This command is not executable in the current status (execution error)
21  The buffer has overflowed, so commands cannot be executed
22  An error occurred while loading or saving parameters, so commands cannot be executed
23  Commands sent from RS-232C cannot be received because AutoID Network Navigator is being connected.
99  SR-1000 Series may be faulty. Please contact your nearest KEYENCE sales office
"""

# Read Serial Number
TRIGGER_INPUT_ON = b'LON\r'
TRIGGER_INPUT_OFF = b'LOFF\r'

REQUEST_SCANNER_VERSION = b'KEYENCE\r'

# Response: OK,FTUNE
REQUEST_FOCUS_ADJUSTMENT = b'FTUNE\r'

# Set the timing mode to "One-shot trigger" (one-shot signal trigger)
SET_ONESHOT_TRIGGER = b'WP,101,1\r'
# Check if one shot trigger is set
IS_ONESHOT_TRIGGER = b'RP,101\r'

SET_TIME_APPENDING = b'WP,300,1\r'
IS_TIME_APPENDING = b'RP,300,1\r'

""" Response:
OK,NUM,a,b,c,d,e
    a: OK count
    b: NG count
    c: ERROR count
    d: STABLE count
    e: Trigger input count (0 to 65535)
"""
READING_HISTORY = b'NUM\r'

""" Response:
OK,ERRSTAT,m
    m = None:       No error
        system:     System error
        update:     Update error
        cfg:        Set value error
        ip:         IP address duplication
        over:       Buffer overflow
        plc:        PLC link error
        profinet:   PROFINET error
        lua:        Script error
        hostconnect: Host connection erro
"""
GET_ERROR_STATUS = b'ERRSTAT\r'

# -----------------------------------------------------------------------------

SOCKET_TIMEOUT = 10

"""
Class for Communication with an SR1000 Keyence Scanner
"""
class Scanner:

    def __init__(self, ip_addr, port, logger=None):
        self.ip = ip_addr
        self.port = port
        self.conn = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.conn.settimeout(SOCKET_TIMEOUT)
        self.connected = False
        self.log = logger
        self.connect()

    # Initiate a connection to the SR1000
    def connect(self):
        try:
            self.conn.connect((self.ip, self.port))  # connect to the server
            sleep(.1)
            # print(self.rx())
            self.connected = True
            print(f"Connected to {self.ip}:{self.port}")
        except socket.error as er:
            print(f"Connection Failed: {er}")

    def tx(self, message):
        self.conn.sendall(message)  # send message

    def rx(self):
        data = self.conn.recv(1024).decode()  # receive response
        # print('Received from scanner: ' + data)
        return data

    # Send/Receive a Message
    def req(self, message):
        self.tx(message)  # send TCP message
        sleep(.1)
        data = self.rx()  # recieve TCP message
        return data
        # self.conn.close()  # close the connection

    # Request Status
    def get_status(self):
        # return self.req(REQUEST_STATUS)
        pass

    # Send/Receive a Message
    def read_code(self):
        return str(self.req(TRIGGER_INPUT_ON))

    def error_type(self):
        return self.req(GET_ERROR_STATUS).replace('OK,ERRSTAT,', '')

if __name__ == '__main__':

    from dotenv import dotenv_values
    c = dotenv_values()

    s = Scanner(c["SR1000_SCANNER_IP"], int(c["SR1000_SCANNER_PORT"]))

    try:

        while True:
            # Fetch serial number
            print(s.read_code())
            sleep(1)

    except KeyboardInterrupt:
        print("Done")


from models.commands.command_register import command_register

class command_args:

    def __init__(self, unique_id: str, name: str, type_var: str, optional_alias = False, required = False, help = False):
        self.unique_id = unique_id
        self.name = name
        self.type_var = self.getType(type_var)
        self.help = help
        self.required = required
        self.optional_alias = optional_alias

    def getType(self,type_var):
        return ({
            'str' : str,
            'int' : int
        }[type_var])


import numpy as np

class SEIRC(object):
    """ This class represents the SEIR epdidemiological model with added clinical estimates.
    See https://en.wikipedia.org/wiki/Compartmental_models_in_epidemiology#The_SEIR_model for the classical SEIR model.

    tldr: self.par; self.simulate(); self.result

    The 'removed' population from the clasical SEIR is split between mild and severe cases which recover at different
    timescales. All mild cases recover. Some severe cases become fatal. Fraction of severe cases which become fatal depends on healthcare
    system load. If threre are more severe cases than the healthcare can handle then the 'overflow'severe cases,
    which cannot fit into the healthcare capacity, have increased fatality rate.

    Parameters for the simulation are given in self.par dictionary. This can be passed to the SEIRC object upon creation or changed later.
    The dictionary looks like this:
    if self.par == None:
            self.par = {'N': 70e6, # total population
                        'I0' : 1, # number of initial infected individuals
                        'R' : 3.5, #reproduction number
                        'T_inc' : 5.2, # incubation time - no symptoms, not infectious
                        'T_inf' : 2.9, # infectious time - no symptoms, infectious
                        'T_mild' : 14, # recovery time for mild cases
                        'T_severe' : 30, # recovery time for severe cases
                        'p_mild' : 0.8, # probability of a mild case
                        'p_fatal' : 0.02, # probability of a fatal case
                        'p_fatal_overrun' : 0.05, # probability of a fatal case in an overrun healthcare system
                        'healthcare_capacity' : 30e3, # maximum number of severe cases that the healthcare system can handle
                        'intervention' : True, # do we do an intervention
                        'R_intervention' : [0.7,1.3], # R after intervention
                        'intervention_intervals' : [[50,300], [300,600]], # during which time intervals we do the intervention [start, stop]
                        'start_day' : 0, # first day of the outbreak - change this to shift time axis of the simulation
                        'duration' : 600 # duration of the simulation
                       }

    p_mild is probability that a random individual ends up in the mild category if infected (probability for ending up in the severe category
    is calculated automatically as 1-p_mild)

    p_fatal is probability that a random individual dies if infected and if given all available help from healthcare. Probability that
    an individual in severe category dies is calculated from this and p_mild and load on the
    healthcare system. Make sure that p_fatal<1-p_mild

    p_fatal_overrun is probability that a random individual dies if infected and not given all available help from healthcare
    (too many patients, go home, good luck). Probability that an individual in severe category dies is calculated from this, p_mild and load on the
    healthcare system. Make sure that p_fatal_overrun<1-p_mild

    healthcare_capacity is the maximu number of severe cases that the healthcare system can handle at one time. Any extra severe cases end up
    in the overrun category and have an increased probability of dying.

    intervention - if set to True, R is going to be changed according to R_intervention and intervention_intervals. If it is false R stays constant.

    'R_intervention' is a list of R values during intervention intervals.

    intervention_intervals is a list of [start,stop] intervals during which the corresponding R values are applied (first R during first interval, second R during the second...).
    Outside of the intervals R goes back to its original value.
    R_intervention needs to have the same number of items as intervention_intervals. The intervals should not overlap.

    Simulation is run with self.simulate(dt = 0.01). If any of the characteristic times is less than 1 consider using shorter dt than the default.
    Results are written in self.result - a dict with arrays for time, susceptible, exposed, infected, mild, severe, dead and recovered populations.
    Removed population from the clasical SEIR and the R values are given as well. The healthcare load is also given - this is ratio of (active severe cases)/(healthcare capacity)

    """
    def __init__(self, parameters = None):
        self.par = parameters

        # check if we got parameter dictionary, if not, create one
        # we should also check whether the par dict is valid but this can be added later...
        if self.par == None:
            self.par = {'N': 70e6, # total population
                        'I0' : 1, # number of initial infected individuals
                        'R' : 3.5, #reproduction number
                        'T_inc' : 5.2, # incubation time - no symptoms, not infectious
                        'T_inf' : 2.9, # infectious time - no symptoms, infectious
                        'T_mild' : 14, # recovery time for mild cases
                        'T_severe' : 30, # recovery time for severe cases
                        'p_mild' : 0.8, # probability of a mild case
                        'p_fatal' : 0.02, # probability of a fatal case
                        'p_fatal_overrun' : 0.05, # probability of a fatal case in an overrun healthcare system
                        'healthcare_capacity' : 30e3, # maximum number of severe cases that the healthcare system can handle
                        'intervention' : True, # do we do an intervention
                        'R_intervention' : [0.7,1.3], # R after intervention
                        'intervention_intervals' : [[50,300], [300,600]], # during which time intervals we do the intervention [start, stop]
                        'start_day' : 0, # first day of the outbreak - change this to shift time axis of the simulation
                        'duration' : 600 # duration of the simulation
                       }
        # calculate p_severe
        self.par['p_severe'] = 1 - self.par['p_mild']

    def isInInterval(self, x, interval):
        """Interval is expected to look like this [start, stop]. Returns True if start<x<stop and False otherwise."""
        value = False
        if x > interval[0] and x < interval[1]:
            value = True
        return value

    def isInIntervals(self, x, intervals):
        """Returns true if x is in any interval contained in intervals."""
        value = False
        for interval in intervals:
            if self.isInInterval(x, interval):
                value = True
        return value

    def getR(self, x):
        """Returns value of R for given time x."""
        R = self.par['R']
        for R_int, interval in zip (self.par['R_intervention'], self.par['intervention_intervals']):
            if self.isInInterval(x, interval):
                R = R_int
        return R

    def simulate(self,  dt = 0.01):
        """Runs a simulation with parameters given in self.par. Results are stored in self.result."""
        duration = self.par['duration']
        steps = int(duration/dt)

        t = np.zeros(steps)    # time
        t[0] = self.par['start_day']
        su = np.zeros(steps)   # susceptible population
        su[0] = self.par['N']  # initial susceptible population
        e = np.zeros(steps)    # exposed population
        i = np.zeros(steps)    # infectious population
        i[0] = self.par['I0']  #initial infectious population
        m = np.zeros(steps)    # population in mild state
        s = np.zeros(steps)    # population in severe state within healthcare
        r = np.zeros(steps)    # recovered
        d = np.zeros(steps)    # dead
        rm = np.zeros(steps)   # removed - people who show symptoms and are excluded from population: sum of ill(in any state) recovered and dead
        rv = np.zeros(steps)   # R values

        N = self.par['N']
        R0 = self.par['R']
        T_inc = self.par['T_inc']
        T_inf = self.par['T_inf']
        T_m = self.par['T_mild']
        T_s = self.par['T_severe']
        p_m = self.par['p_mild']
        p_s = self.par['p_severe']
        p_f = self.par['p_fatal']/self.par['p_severe']          # probability that a severe case turns fatal, mild cases do not turn fatal
        p_fo = self.par['p_fatal_overrun']/self.par['p_severe'] # probability that a severe case outside healthcare system turns fatal

        #p_so = self.par['p_severe_overrun']
        hc =self.par['healthcare_capacity']
        intervention = self.par['intervention']
        R1 = self.par['R_intervention']

        R = R0
        #print('p_f = ' + str(p_f))
        #print('p_fo = ' + str(p_fo))
        for n in range(steps-1):
            t[n+1] = t[n] + dt

            # calculate effective probability that severe case is fatal
            p_fe = p_f
            if s[n] > hc: #if healthcare system is overrun
                p_fe = (p_f*hc + p_fo*(s[n]-hc))/s[n] # weighted average of fatality rates inside and outside healthcare system

            #if t[n]%10 < 0.05:
            #    print p_fe

            if intervention:
                R = self.getR(t[n])

            # calculate changes in individual populations
            dsu = -R/(T_inf*N)*su[n]*i[n]*dt              # change of susceptible population
            de = (R*su[n]*i[n]/(T_inf*N) - e[n]/T_inc)*dt # change of exposed population
            di = (e[n]/T_inc - i[n]/T_inf)*dt             # change of infectious population
            drm = i[n]/T_inf*dt                           # change of removed population
            dm = (i[n]/T_inf*p_m - m[n]/T_m)*dt           # change of mild population
            ds = (i[n]/T_inf*p_s - s[n]/T_s)*dt           # change of severe population
            dr = (m[n]/T_m + (1 - p_fe)*s[n]/T_s)*dt      # change of recovered population
            dd = (p_fe*s[n]/T_s)*dt                       # change of dead population

            # propagate to the next step
            su[n+1] = su[n] + dsu
            e[n+1] = e[n] + de
            i[n+1] = i[n] + di
            rm[n+1] = rm[n] + drm
            m[n+1] = m[n] + dm
            s[n+1] = s[n] + ds
            r[n+1] = r[n] + dr
            d[n+1] = d[n] + dd
            rv[n] = R

        rv[-1] = R

        out = {"time":t,
              "susceptible": su,
              'exposed':e,
              'infectious':i,
              'mild':m,
              'severe':s,
              'recovered':r,
              'dead' : d,
              'removed':rm,
              'healthcare load': s/hc,
              'R' : rv}

        self.result = out


#!/usr/bin/python3
# -*- coding: utf-8 -*-
# @Author    : Nam Zeng
# @Time      : 2018/12/6 10:38
# @Desc      : 用牛顿插值法拟合经过给定点的曲线

#  x0 | y0
#  x1 | y1  y10
#  x2 | y2  y21   y210

import matplotlib.pyplot as plt
import numpy as np

def newton_interpolate(x, y):
    """
    :param x: 点集的x坐标集x[]
    :param y: 点集的y坐标集y[]
    :return: 拟合出的曲线系数列表coe[]
    """

    n = len(x)
    mat = np.zeros((n, n), dtype=float)

    # 将矩阵的第一列元素分别赋值为y[i]
    for i in range(n):
        mat[i][0] = y[i]

    for i in range(1, n):  # 对于第1~n-1列
        for j in range(i, n):  # 每列从上到下填入各差商值，即从三角形的斜边到直角边
            mat[j][i] = (mat[j][i - 1] - mat[j - 1][i - 1]) / (x[j] - x[j - i])

    # 输出对角线系数
    coe = []
    for i in range(n):
        coe.append(mat[i][i])

    return coe

def draw(x, y, coe):
    # 画图
    plt.title("Newton Interpolation")
    plt.scatter(x, y, label="discrete data", color='green')
    ltx = np.linspace(-5, 5, 300)
    lty = [coe[0] + coe[1] * (xi - x[0]) + coe[2] * (xi - x[0]) * (xi - x[1])
           + coe[3] * (xi - x[0]) * (xi - x[1]) * (xi - x[2])
           for xi in ltx]
    plt.plot(ltx, lty, label="fitting curve", color='red')
    plt.legend(loc="upper right")
    plt.show()

if __name__ == '__main__':
    # 设置拟合数据,求三次牛顿多项式
    x = [3, 2, -1, -2]
    y = [-23, -4, 5, 12]
    coe = newton_interpolate(x, y)
    print("拟合曲线的系数(b0~bn-1)：\n", coe)
    draw(x, y, coe)


#!usr/bin/env python
# coding:utf-8
###############################################################################################
#THIS SCRIPT IS SPECIFICALLY CREATED FOR A DNS_EXFILTRATION CHALLENGE IN WHICH THE EXFILTRATED#
#DATA WAS A PNG IN HEXA AS THE CNAME IN THE DNS RESPONSES ON THE CNAME.DOMAIN. IT SHOULD BE   #
#ADAPTED TO YOUR NEEDS BEFOR USING IT                                                         #
###############################################################################################

import re
from scapy.all import rdpcap, DNSQR, DNSRR

i=0
last = b''

with open("<a.out>", "wb") as f: #  <---- REPLACE HERE a.out by your expected output. Is it an image? text to be deobfuscated?
    for p in rdpcap('<dns_file.pcap>'): # <---- REPLACE HERE <dns_file.pcap> by the pcap to be analyzed
        if p.haslayer(DNSQR) and not p.haslayer(DNSRR):
            qry = p[DNSQR].qname.decode('utf-8').replace("<domain>","").split(".") #<---- REPLACE HERE <domain> by the domain used in the exchange
            qry = bytes.fromhex(''.join(qry))[9:] # In this specific case we need to pop out the prefix generated by the exfiltration tool (DNSCAT2) : 9 bytes

            if last == qry or qry == b'':
                continue
            elif i == 0 :
                f.write(qry[8:]) # Specific to our case. A flag was set to instantiate the begining of the exfiltrated data. We delete ot.
            else:
                f.write(qry)

            i+=1 # Needed to treat the specific lines (Here line 0). It could has been managed at thend though.
            last=qry # The exfiltrate could volontarely duplicate packets in order to harden the data compilation. Here we skip them thanks to this variable.

return query


# *_*coding:utf-8 *_*
"""
这个脚本是用来压缩数据，减少内存占用的。压缩数据有几种方法：

第一种：
当我们明确知道要加载数据的范围，使用pd.read_table读取数据时，可以用其中的dtype参数来手动指定类型。比如某一列的数据范围肯定在0~255之中，那么我们可以指定为np.uint8类型。

第二种：
如果数据列数太多，或者不清楚数据具体范围的话下面是一个脚本，可以自动判断类型，并根据类型修改数据范围。

第三种：
批量处理，增量训练模型。
"""
import numpy as np

def reduce_mem_usage(props):
    # 计算当前内存
    start_mem_usg = props.memory_usage().sum() / 1024 ** 2
    print("Memory usage of the dataframe is :", start_mem_usg, "MB")
    # 哪些列包含空值，空值用-999填充。why：因为np.nan当做float处理
    NAlist = []
    for col in props.columns:
        # 这里只过滤了objectd格式，如果你的代码中还包含其他类型，请一并过滤
        if (props[col].dtypes != object):
            # 判断是否是int类型
            isInt = False
            mmax = props[col].max()
            mmin = props[col].min()
            # Integer 不支持 NA,所以要填充
            if not np.isfinite(props[col]).all():
                NAlist.append(col)
                props[col].fillna(-999, inplace=True)  # 用-999填充
            # 试试看能不能转int
            asint = props[col].fillna(0).astype(np.int64)
            result = np.fabs(props[col] - asint)
            result = result.sum()
            if result < 0.01:  # 绝对误差和小于0.01认为可以转换的，要根据task修改
                isInt = True
            # 生成整数/无符号的int类型
            if isInt:
                if mmin >= 0:  # 最小值大于0，转换成无符号整型
                    if mmax <= 255:
                        props[col] = props[col].astype(np.uint8)
                    elif mmax <= 65535:
                        props[col] = props[col].astype(np.uint16)
                    elif mmax <= 4294967295:
                        props[col] = props[col].astype(np.uint32)
                    else:
                        props[col] = props[col].astype(np.uint64)
                else:  # 转换成有符号整型
                    if mmin > np.iinfo(np.int8).min and mmax < np.iinfo(np.int8).max:
                        props[col] = props[col].astype(np.int8)
                    elif mmin > np.iinfo(np.int16).min and mmax < np.iinfo(np.int16).max:
                        props[col] = props[col].astype(np.int16)
                    elif mmin > np.iinfo(np.int32).min and mmax < np.iinfo(np.int32).max:
                        props[col] = props[col].astype(np.int32)
                    elif mmin > np.iinfo(np.int64).min and mmax < np.iinfo(np.int64).max:
                        props[col] = props[col].astype(np.int64)
            # 注意：这里对于float都转换成float16，需要根据自己的情况自己更改
            else:
                props[col] = props[col].astype(np.float16)

    mem_usg = props.memory_usage().sum() / 1024 ** 2
    print("Memory usage is: ", mem_usg, " MB")
    print("This is ", 100 * mem_usg / start_mem_usg, "% of the initial size")
    return props, NAlist


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
'''
Conjunto de funciones desarrolladas para ser utilizadas en los programas principales
'''
import numpy as np
import torch
import matplotlib.pyplot as plt

from labcomdig import gray2de
from network import *

'''
NUMPY
'''
def transmisorpamV2(Bn,Eb,M,p,L):
    """
    [Xn,Bn,An,phi,alfabetopam] = transmisorpamV2(Bn,Eb,M,p,L)

    Entradas:
     Bn = Secuencia de dígitos binarios
     Eb = Energía media por bit transmitida en Julios
     M  = Número de síímbolos del código PAM
     p  = Pulso paso de baja o paso de banda

     L  = Número de puntos a utilizar en la representación de un símbolo

    Devuelve:
     Xn = la señal de información (discreta)
     Bn = La secuencia de dígitos binarios realmente transmitidos
     An = La secuencia de niveles de amplitud transmitidos
   	 phi = Pulso básico real normalizado (energía unidad)
     alfabetopam = Los niveles de amplitud asociados a cada símbolo
    """
    # Comprobación de parámetros de entrada
    p=np.reshape(p,[np.size(p)])  # <=   #p=p.squeeze()
    if len(Bn)<1 or Eb<=0 or M<2 or np.dot(p,p)==0 or L<1:
        raise Exception('Error: revise los parámetros de entrada')
    # Se obtienen en primer lugar los niveles asociado a cada símbolo ¿Cuántos bits hay en cada símbolo?
    k = int(np.ceil(np.log2(M)))
    M = 2**(k) # Se Ajusta M a una potencia de dos

    # El alfabeto [Ver la ecuación (4.21)]
    alfabetopam = np.sqrt(3*Eb*np.log2(M)/(M**2-1))*(2*(np.arange(M))-M+1)

    # Si Bn no tiene una longitud múltiplo de k, se completa con ceros
    Nb = len(Bn)  # Número de bits a transmitir, actualizado
    Bn = Bn.squeeze().astype(int) #Comprobación de int y dimensiones
    Bn = np.r_[Bn,np.zeros(int(k*np.ceil(Nb/k)-Nb)).astype(int)] #
    Nb = len(Bn)  # Número de bits a transmitir tras la corrección
    Ns = Nb//k # Número de símbolos a transmitir

    # La secuencia generada
    if M>2:
        An = alfabetopam[gray2de(np.reshape(Bn,[Ns,k]))]
    else:
        An = alfabetopam[Bn]

    # Comprobación de las longitudes y otros datos del pulso suministrado para
    # hacer que el número de muestras del mismo sea efectivamente L
    Ls = len(p)
    if Ls<L:
        p = np.r_[p, np.zeros(L-Ls)]
    elif Ls>L:
        print('La duración del pulso se trunca a {} muestras'.format(str(L)))
        p = p[:L] #Debe modificarse si se quiere un pulso de más de L muestras
    # Se normaliza la energía del pulso para obtener la base del sistema
    phi = p / np.sqrt(p@p)

    # Obtención del tren de pulsos, Xn = np.kron(An,phi) ó
    Xn = np.reshape(np.reshape(An,[Ns,1])*phi,[Ns*L,]) #Debe modificarse si se quiere un pulso de más de L muestras
    return [Xn,Bn,An,phi,alfabetopam]

def de2Nary(d,n,N):
    """
    b = de2Nary(d,n,N)
    Convierte un número decimal, d, en un vector binario, b, de longitud n
    con base N
    """
    c = np.zeros([len(d),int(n)])
    for i in range(int(n)): d, c[:,i] = np.divmod(d,N)
    c = np.fliplr(c);
    return c.astype(int).T

'''
TORCH
'''

def t_Qfunct(x):
    """
     y = Qfunct(x) evalúa la función Q en x.
    Donde y = 1/sqrt(2*pi) * integral desde x hasta inf de exp(-t^2/2) dt
    """
    y=(1/2)*torch.special.erfc(x/(torch.tensor(2)**.5))
    return y

def t_gray2de(b):
    """
    Convierte cada columna de la matriz formada por dígitos binarios b en un vector
        fila de los valores decimales correspondientes, aplicando codificación de Gray.
    """
    if not torch.is_tensor(b):
        raise Exception('Error: la entrada no es un tensor')
    b = b.long() #Aseguro que sea tipo int
    c = torch.zeros_like(b); c[:,0] = b[:,0]
    for i in range(1,b.size(dim=1)):
        c[:,i] = torch.logical_xor(c[:,i-1], b[:,i])
    c = torch.fliplr(c) # Convierte los bits menos significativos en los más significativos
    #Comprueba un caso especial.
    [n,m] = c.size()
    if torch.min(torch.tensor((m,n))) < 1:
        d = []
        return
    d = c @ 2**torch.arange(m)
    return d

def t_de2gray(d,n):
    """
    b = de2gray(d,n)
    Convierte un número decimal, d, en un vector binario, b, de longitud n
    """
    c = torch.zeros(len(d),int(n))
    for i in range(int(n)):
        c[:,i] = torch.fmod(d,2) #resto
        d = torch.div(d,2,rounding_mode='floor') #cociente
    c = torch.fliplr(c); b = torch.zeros_like(c); b[:,0] = c[:,0]; aux = b[:,0]
    for i in range(1,int(n)):
        b[:,i] = torch.logical_xor(aux, c[:,i])
        aux = torch.logical_xor(b[:,i], aux)
    return torch.reshape(b,[-1]).long()

def t_simbolobit(An,alfabeto):
    """
    Bn = simbolobit(An, alfabeto)
    An = secuencia de símbolos pertenecientes al alfabeto
    alfabeto = tabla con los símbolos utilizados en la transmisión
    Bn = una secuencia de bit, considerando que los símbolos se habían
    generado siguiendo una codificación de Gray
    """

    k = torch.log2(torch.tensor(len(alfabeto))) # bits por símbolo

    if k>1:
        distancia = abs(alfabeto[0]-alfabeto[1])
        indices   = torch.round((An-alfabeto[0])/distancia)
        #Bn        = torch.reshape(t_de2gray(indices,k),int(k*len(An)))
        Bn = t_de2gray(indices,k)
    else:
        Bn = ((An/max(alfabeto))+1)/2

    return Bn

def t_detectaSBF(r,alfabeto):
    """
    An = detectaSBF(r,alfabeto)
    r = secuencia a la entrada del detector, con estimaciones de s_i
    alfabeto = tabla con los niveles de amplitud/símbolos

    Genera:
    An = una secuencia de símbolos pertenecientes al alfabeto de acuerdo con
    una regla de distancia euclidiana mínima (mínima distancia)
    """

    # Obtiene el índice respecto al alfabeto
    r_repeated = torch.repeat_interleave(r.reshape(r.size(dim=0),1),alfabeto.size(dim=0),1)
    ind = torch.argmin(torch.abs(r_repeated - alfabeto), 1)

    # Genera la secuencia de niveles detectados
    An = alfabeto[ind]

    return An

def t_transmisorpamV2(Bn,Eb,M,p,L):
    """
    [Xn,Bn,An,phi,alfabetopam] = transmisorpamV2(Bn,Eb,M,p,L)

    Entradas:
     Bn = Secuencia de dígitos binarios
     Eb = Energía media por bit transmitida en Julios
     M  = Número de síímbolos del código PAM
     p  = Pulso paso de baja o paso de banda
     L  = Número de puntos a utilizar en la representación de un símbolo

    Devuelve:
     Xn = la señal de información (discreta)
     Bn = La secuencia de dígitos binarios realmente transmitidos
     An = La secuencia de niveles de amplitud transmitidos
   	 phi = Pulso básico real normalizado (energía unidad)
     alfabetopam = Los niveles de amplitud asociados a cada símbolo
    """
    # Paso a tensores de los parámetros de entrada
    Eb = torch.tensor(Eb)
    M = torch.tensor(M)
    L = torch.tensor(L)

    # Comprobación de parámetros de entrada
    p=torch.reshape(p,p.size())  # <=   #p=p.squeeze()
    if Bn.size(dim=1)<1 or Eb<=0 or M<2 or torch.dot(p,p)==0 or L<1:
        raise Exception('Error: revise los parámetros de entrada')
    # Se obtienen en primer lugar los niveles asociado a cada símbolo ¿Cuántos bits hay en cada símbolo?
    k = torch.ceil(torch.log2(M))
    M = 2**(k) # Se Ajusta M a una potencia de dos

    # El alfabeto [Ver la ecuación (4.21)]
    alfabetopam = torch.sqrt(3*Eb*torch.log2(M)/(M**2-1))*(2*(torch.arange(M))-M+1)

    # Si Bn no tiene una longitud múltiplo de k, se completa con ceros
    Nb = torch.tensor(Bn.size(dim=1))  # Número de bits a transmitir, actualizado
    Bn = Bn.squeeze().long() #Comprobación de int y dimensiones
    Bn = torch.hstack((Bn,torch.zeros((k*torch.ceil(Nb/k)-Nb).long()))) #
    Nb = torch.tensor(Bn.size(dim=0))  # Número de bits a transmitir tras la corrección
    Ns = torch.div(Nb,k,rounding_mode='trunc') # Número de símbolos a transmitir

    # La secuencia generada
    if M>2:
        An = alfabetopam[t_gray2de(torch.reshape(Bn,(int(Ns),int(k))))]
    else:
        An = alfabetopam[Bn.long()]

    # Comprobación de las longitudes y otros datos del pulso suministrado para
    # hacer que el número de muestras del mismo sea efectivamente L
    Ls = p.size(dim=0)
    if Ls<L:
        p = torch.hstack(p,torch.zeros(L-Ls))
    elif Ls>L:
        print('La duración del pulso se trunca a {} muestras'.format(str(L)))
        p = p[:L] #Debe modificarse si se quiere un pulso de más de L muestras
    # Se normaliza la energía del pulso para obtener la base del sistema
    phi = p / torch.sqrt(p@p)

    # Obtención del tren de pulsos, Xn = np.kron(An,phi) ó
    a = torch.reshape(An,(int(Ns),1))*phi
    Xn = torch.reshape(a,(int(Ns)*L,)) #Debe modificarse si se quiere un pulso de más de L muestras
    return [Xn,Bn.long(),An,phi,alfabetopam]

def t_de2Nary(d,n,N):
    """
    b = de2Nary(d,n,N)
    Convierte un número decimal, d, en un vector binario, b, de longitud n
    con base N
    """
    c = torch.zeros(len(d),int(n))
    for i in range(int(n)):
        c[:,i] = torch.fmod(d,N) #resto
        d = torch.div(d,N,rounding_mode='floor') #cociente
    c = torch.fliplr(c);
    return c.long().T

'''
NEURAL NETWORK
'''

def correction(Nb_train,Nb_test,k,M,n):
    """
    [Nb_train,Ns_train,Nb_test,Ns_test] = correction(Nb_train,Nb_test,k,M,n)

    Cambia el valor de Nb_train y Nb_test si es necesario para
    que sea múltiplo de k y M**n
    """

    Ns_train = int(torch.floor(torch.tensor(Nb_train/(k*M**n)))) #Number of symbols (multiple of k and M**n)
    Nb_train = int(Ns_train*(k*M**n)) #Number of bits on transmission
    Ns_train = int(torch.floor(torch.tensor(Nb_train/k))) #Number of symbols (multiple of k)

    Ns_test = int(torch.floor(torch.tensor(Nb_test/k))) #Number of symbols (multiple of k)
    Nb_test = int(Ns_test*k)          #Number of bits on transmission

    return [Nb_train,Ns_train,Nb_test,Ns_test]

def create_datasets(H,Nb_train,Nb_test,Eb,M,n,SNRdb,batch_size,valid_size):
    """
    [trainloader,validloader,testloader,x,alphabet] = create_datasets(H,Nb_train,Nb_test,Eb,M,n,SNRdb,batch_size,valid_size)

    Devuelve los conjuntos de datos listos para itererar, además del conjunto
    de combinaciones posibles y el alfabeto de la constelación
    """
    # Data generation for training
    [Rn,Cn,Bn,x,alphabet] = generate_data(H,Nb_train,Eb,M,n,SNRdb,train=True)
    trainset = SymbolsDataset(Rn,Cn,Bn)

    # Data generation for testing
    [Rn,Cn,Bn,x,alphabet] = generate_data(H,Nb_test,Eb,M,n,SNRdb,train=False)
    testset = SymbolsDataset(Rn,Cn,Bn)

    # Indixes used for validation
    num_train = len(trainset)
    split = int(np.floor(valid_size * num_train))

    # Split data
    train_split, valid_split = SymbolsDataset.split_data(trainset,split)

    # Load training data in batches
    trainloader = torch.utils.data.DataLoader(train_split,
                                              batch_size=batch_size,
                                              num_workers=0)

    # Load validation data in batches
    validloader = torch.utils.data.DataLoader(valid_split,
                                              batch_size=batch_size,
                                              num_workers=0)

    # Load test data in batches
    testloader = torch.utils.data.DataLoader(testset,
                                             batch_size=batch_size,
                                             num_workers=0)

    return [trainloader,validloader,testloader,x,alphabet]

def generate_data(H,Nb,Eb,M,n,SNRdb,train):
    """
    [Xn/Rn,Bn,Cn,x,alphabet] = generate_data(H,Nb,Eb,M,n,SNRdb,test)

    Entradas:
     H     = Matriz del canal
     Nb    = Número de bits a transmitir
     Eb    = Energía media por bit transmitida en Julios
     M     = Número de símbolos del código PAM
     n     = Número de antenas del sistema MIMO nxn
     SNRdb = Signal to Noise Ratio
     test  = Booleano para determinar si son datos para entrenar o testar

    Devuelve:
     Rn = La señal de información (discreta) recibida
     Bn = La secuencia de dígitos binarios realmente transmitidos
     Cn = La combinación correspondiente a cada valor de An
     x  = La matriz con las posibles combinaciones
     alphabet = Los niveles de amplitud asociados a cada símbolo
    """

    k = int(torch.log2(torch.tensor(M)))      #Number of bits on each symbol
    Ns = int(torch.floor(torch.tensor(Nb/k))) #Number of symbols (multiple of k)

    alphabet = torch.sqrt(3*Eb*torch.log2(torch.tensor(M))/(M**2-1))*(2*(torch.arange(M))-M+1)

    # possible combinations
    ind = t_de2Nary(torch.arange(M**n),n,M)
    x = alphabet[ind]

    if train:  # data for training. same number of symbols transmitted

        Xn = x.tile(int(Ns/x.size(1)))
        Xn = Xn[:,torch.randperm(Xn.size(1))] # Shuffle
        Bn = t_simbolobit(Xn.flatten(),alphabet) #Detected bits

        #Data reshape. Each row represents an antenna
        Bn = Bn.reshape(n,Nb)

        #Index of the combinations
        Cn = getidx(x,Xn,n)

    else:     # data for testing

        # fixed seed for same results always
        torch.manual_seed(1)

        bn = torch.randint(0,2,(1,Nb*n)) #Bits to transmit
        [Xn,Bn,An,phi,alphabet] = t_transmisorpamV2(bn,Eb,M,torch.ones(1),1)

        #Data reshape. Each row represents an antenna
        Xn = Xn.reshape(n,Ns)
        Bn = Bn.reshape(n,Nb)
        An = An.reshape(n,Ns)

        #Index of the combinations
        Cn = getidx(x,An,n)

    SNR = 10**(SNRdb/10)   #Signal to Noise Ratio [n.u.]
    varzn = Eb/(2*SNR)         #Noise variance
    #if varzn <= 0.0025: varzn = torch.tensor(0.0025)
    Wn = torch.sqrt(varzn)*torch.randn(*Xn.shape)  #AWGN

    Rn = H@Xn + Wn

    return [Rn,Cn,Bn.long(),x,alphabet]

def getidx(x,An,n):
    """
    idx = getidx(x,An,n)

    Función que retorna el número de combinación correspondiente a cada
    valor de An

    x  = La matriz con las posibles combinaciones
    An = La secuencia de niveles de amplitud transmitidos
    n  = El número de antenas
    """

    idx = torch.empty(0)
    for col in An.T:
        i = (sum(x==col.view(n,1)) == n).nonzero(as_tuple=True)[0]
        idx = torch.hstack((idx,i))
    return idx.long()

def train_model(model, trainloader, validloader, optimizer, criterion, patience, n_epochs):
    """
    [model, avg_train_losses, avg_valid_losses] = create_datasets(H,Nb_train,Nb_test,Eb,M,n,SNRdb,batch_size,valid_size):

    Función que entrena a la red y retorna el modelo y los errores cometidos

    """
    min_valid_loss = np.inf
    es_counter = 0

    # loss per batch
    train_losses = []
    valid_losses = []

    # loss per epoch
    avg_train_losses = []
    avg_valid_losses = []

    for epoch in range(1, n_epochs + 1):

        # Train the model
        model.train()
        for symbols, combs, bits in trainloader:
            # clear the gradients
            optimizer.zero_grad()
            # forward pass
            output = model(symbols)
            # calculate the loss
            loss = criterion(output, combs)
            # backward  pass
            loss.backward()
            # perform a single optimization step
            optimizer.step()
            # record training loss
            train_losses.append(loss.item())

        # Validate the model
        model.eval()
        for symbols, combs, bits in validloader:
            # forward pass
            output = model(symbols)
            # calculate the loss
            loss = criterion(output, combs)
            # record valid loss
            valid_losses.append(loss.item())

        # calculate average loss over an epoch
        train_loss = np.average(train_losses)
        valid_loss = np.average(valid_losses)
        avg_train_losses.append(train_loss)
        avg_valid_losses.append(valid_loss)

        if min_valid_loss > valid_loss:
            min_valid_loss = valid_loss
            # Saving State Dict
            torch.save(model.state_dict(), 'saved_model.pth')
        else:
            es_counter += 1
            if es_counter == patience: break

        # load saved model
        model.load_state_dict(torch.load('saved_model.pth'))

        # clear lists to track next epoch
        train_losses = []
        valid_losses = []

    return [model, avg_train_losses, avg_valid_losses]

def printloss(avg_train_losses,avg_valid_losses,SNR,save):
    """
    Función para mostrar por consola las pérdidas de entramiento y validación
    en un epoch
    """

    fig = plt.figure(1,figsize=(10,8))
    plt.plot(range(1,len(avg_train_losses)+1),avg_train_losses, label='Training Loss')
    plt.plot(range(1,len(avg_valid_losses)+1),avg_valid_losses,label='Validation Loss')

    # find position of lowest validation loss
    minposs = avg_valid_losses.index(min(avg_valid_losses))+1
    plt.axvline(minposs, linestyle='--', color='r',label='Early Stopping Checkpoint')

    plt.xlabel('epochs')
    plt.ylabel('loss')
    plt.ylim(0, 2) # VARIABLE scale
    plt.xlim(0, len(avg_valid_losses)+1) # consistent scale
    plt.grid(True)
    plt.legend()
    plt.title('SNR = {}dB'.format(SNR))
    plt.tight_layout()
    plt.show()
    if save: fig.savefig('loss_plot.png', bbox_inches='tight')
    pass

def eval_model(model, trainloader, validloader, testloader, x, alphabet, k):
    """
    [berTrain, berValid, berTest] = eval_model(model, trainloader, validloader, testloader, x, alphabet, k)

    Función que evalua el modelo y devuelve las distintas tasas de error binarias
    """

    # BER for saved model with train data
    model.eval()

    berTrain = 0
    for symbols, combs, bits in trainloader:
        # forward pass
        output = model(symbols)
        berTrain += ber(output,x,alphabet,bits,k)
    berTrain /= len(trainloader)

    berValid = 0
    for symbols, combs, bits in validloader:
        # forward pass
        output = model(symbols)
        berValid += ber(output,x,alphabet,bits,k)
    berValid /= len(validloader)

    berTest = 0
    for symbols, combs, bits in testloader:
        # forward pass
        output = model(symbols)
        berTest += ber(output,x,alphabet,bits,k)
    berTest /= len(testloader)

    return [berTrain, berValid, berTest]

def ber(output,x,alphabet,bits,k):
    """
    ber = ber(output,x,alphabet,bits,k)
    Calcula la tasa de error de bit de una señal transmitida

    Entradas:
     output   = Salida de la Red Neuronal (bacth_size x n_combinations)
     x        = La matriz con las posibles combinaciones
     alphabet = Los niveles de amplitud asociados a cada símbolo
     bits     = El conjunto de Bn transmitidos
     k        = Los bits que hay por símbolo
    """

    Nb = k*output.size(0) # Número de bits transmitidos (k*Ns)
    n = x.size(0)         # Núermo de antenas

    val, idx = torch.max(output,1) # El máximo de cada salida

    # Es necesario modificar la variable 'bits' de manera que estén
    # en la forma nxNb, correspondiendo cada fila a la transmisión de una antena
    Bn = torch.empty(0,Nb)
    for i in range(int(n)): Bn = torch.vstack((Bn,bits[:,i,:].flatten()))

    Andetected = x[:,idx]    # Detected symbols
    Bndetected = t_simbolobit(Andetected.flatten(),alphabet).reshape(n,Nb) #Detected bits
    errorsBit = Nb-torch.sum(Bndetected==Bn,axis=1)

    return (errorsBit/Nb).reshape(n,1)

def detect(Rn,Bn,H,alphabet,Nb,Ns,n,method,**kwargs):
    """
    ber = detect(Rn,Bn,H,alphabet,Nb,Ns,n,method,**kwargs)

    Calcula la BER de los métodos ZF, LMMSE y ML
    """
    if method == 'ZF':
        Hinv = kwargs.get('Hinv',None)

        Z = Hinv@Rn

    elif method == 'LMMSE':
        Es = kwargs.get('Es',None)
        varzn = kwargs.get('varzn',None)

        Z = torch.linalg.inv(H.T@H + varzn/Es*torch.eye(n))@H.T@Rn

    elif method == 'ML':
        x = kwargs.get('x',None)

        hx = torch.unsqueeze(H@x,2) #H@x with shape (n,combinations,1)
        Z = x[:,torch.argmin(torch.linalg.norm(Rn.reshape(n,1,Ns)-hx,dim=0),axis=0)]

    Andetected = t_detectaSBF(Z.flatten(),alphabet).reshape(n,Ns) #Detected symbols
    Bndetected = t_simbolobit(Andetected.flatten(),alphabet).reshape(n,Nb) #Detected bits
    errorsBit = Nb-torch.sum(Bndetected==Bn,axis=1) # Nº Errors per bit

    return (errorsBit/Nb).reshape(n,1)


import os
os.system('pip3 install lightgbm==2.1.2')
os.system('pip3 install hyperopt')

import pandas as pd
import pickle
import data_converter
import numpy as np
import scipy
from os.path import isfile
import random
import time
from sklearn.model_selection import train_test_split, StratifiedKFold
from sklearn.metrics import roc_auc_score
import sklearn
from multiprocessing import Pool

from preprocess import *
from boosting import *
from automl import *

module = ['read_data', 'preprocess', 'encode_cat', 'encode_mv', 'fit', 'predict']

class Model:
    def __init__(self, data_info, time_info):
        '''
        This constructor is supposed to initialize data members.
        Use triple quotes for function documentation.
        '''
        self.num_train_samples = 0
        self.num_feat = 1
        self.num_labels = 1

        self.is_trained = False
        self.batch_num = 0
        self.total_batch_num = 10

        self.batch_window_size = 3

        self.y_pred = None
        self.data_memory = []

        self.hyper_param = None

        self.missing_value_preprocess = MissingValuePreprocess(drop_ratio=0.9)

        self.overall_time = np.zeros(10, dtype='float')
        self.module_time = {}
        for m in module:
            self.module_time[m] = np.zeros(10, dtype='float')

    def fit(self, F, y, data_info, time_info):

        # time budget score
        if not self.is_trained:
            weight = [1., 1., 2., 4.]
            feat_weight = 0.0
            for i in range(4):
                feat_weight += weight[i] * data_info['loaded_feat_types'][i]
            self.budget_score = float(data_info['time_budget']) / (y.shape[0] * 10. / 1e6) / feat_weight
            self.time_manager = TimeManager(self.budget_score, self.batch_window_size)
            print ('budget score: %.2f' %(self.budget_score))

            self.use_mv = False
            if (data_info['loaded_feat_types'][3] != 0 and self.budget_score > 3.5):
                self.use_mv = True

        # read data
        if not self.is_trained:
            self.module_time['read_data'][self.batch_num] = time.time() - time_info[1]

        y = y.ravel()

        # preprocessing
        if not self.is_trained:

            module_start = time.time()

            F['numerical'] = self.missing_value_preprocess.fit_transform(F['numerical'], 'numerical', input_type='ndarray')
            F['CAT'] = self.missing_value_preprocess.fit_transform(F['CAT'], 'CAT', input_type='dataframe')
            if self.use_mv:
                F['MV'] = self.missing_value_preprocess.fit_transform(F['MV'], 'MV', input_type='dataframe')

            F['numerical'] = data_converter.replace_missing(F['numerical']).astype('float32')
            F['CAT'] = F['CAT'].fillna('-1')

            module_end = time.time()
            self.module_time['preprocess'][self.batch_num] = module_end - module_start

            self.F = F

        # store current batch of data
        if (len(self.data_memory) == self.batch_window_size):
            del self.data_memory[0]
        self.data_memory.append([self.F, y])

        self.batch_end_time = time.time()
        if self.is_trained:
            self.batch_start_time = self.next_batch_start_time
        else:
            self.batch_start_time = time_info[1]

        self.overall_time[self.batch_num] = self.batch_end_time - self.batch_start_time
        print ('overall time spent on batch %d: %.2f seconds' %(self.batch_num, self.overall_time[self.batch_num]))
        for m in module:
            t = self.module_time[m][self.batch_num]
            ratio = t / self.overall_time[self.batch_num]
            print ('%s: %.2f seconds, %.2f%%' %(m, t, ratio * 100.))
        if self.is_trained:
            print ('time spent ratio: %.2f%%' %(self.time_spent_ratio))

        self.fit_end_time = time.time()
        self.next_batch_start_time = time.time()

    def transferPredict(self, i, data_info, time_info):

        train_num = np.concatenate([self.data_memory[j][0]['numerical'] for j in range(i, len(self.data_memory))], axis=0)
        train_cat = pd.concat([self.data_memory[j][0]['CAT'] for j in range(i, len(self.data_memory))], axis=0, ignore_index=True, copy=False)
        label = np.concatenate([self.data_memory[j][1] for j in range(i, len(self.data_memory))])

        test_num = self.F['numerical']
        test_cat = self.F['CAT']

        module_start = time.time()

        # encode categorical feature
        all_cat = pd.concat([train_cat, test_cat], axis=0, ignore_index=True, copy=False)
        del train_cat, test_cat
        cat_columns = all_cat.columns.copy()

        with Pool(processes=2) as pool:
            cat_encode = pool.map(CATEncoder().fit_transform, [all_cat[[col]] for col in cat_columns])
            pool.close()
            pool.join()
        all_cat = pd.concat(cat_encode, axis=1, copy=False)
        del cat_encode

        train_cat, test_cat = all_cat.iloc[:label.shape[0], :].reset_index(drop=True), all_cat.iloc[label.shape[0]:, :].reset_index(drop=True)
        del all_cat

        module_end = time.time()
        self.module_time['encode_cat'][self.batch_num] = module_end - module_start

        train_num, test_num = pd.DataFrame(train_num), pd.DataFrame(test_num)
        train_feature = [train_num, train_cat]
        test_feature = [test_num, test_cat]

        # encode multi-value feature
        if self.use_mv:

            module_start = time.time()

            max_cat_num = 1000

            all_mv = pd.concat([self.data_memory[j][0]['MV'] for j in range(i, len(self.data_memory))], axis=0)
            all_mv = pd.concat([all_mv, self.F['MV']], axis=0)
            mv_columns = all_mv.columns.copy()

            with Pool(processes=2) as pool:
                mv_encode = pool.map(MVEncoder(max_cat_num=max_cat_num).fit_transform, [all_mv[col] for col in mv_columns])
                all_mv = pd.concat(mv_encode, axis=1)
                pool.close()
                pool.join()

            all_mv = all_mv.astype('int16')
            train_mv, test_mv = all_mv.iloc[:label.shape[0], :].reset_index(drop=True), all_mv.iloc[label.shape[0]:, :].reset_index(drop=True)
            del all_mv

            train_feature.append(train_mv)
            test_feature.append(test_mv)

            module_end = time.time()
            self.module_time['encode_mv'][self.batch_num] = module_end - module_start

        feature_new = pd.concat(train_feature, axis=1)
        for feat in train_feature:
            del feat
        del train_feature

        self.X_new = pd.concat(test_feature, axis=1)
        for feat in test_feature:
            del feat
        del test_feature

        weight = None

        # time spent on data/feature processing in current batch
        time_spent_invariant, time_spent_variant = 0., 0.
        for m in ['read_data', 'preprocess']:
            time_spent_invariant += self.module_time[m][self.batch_num]
        for m in ['encode_cat', 'encode_mv']:
            time_spent_variant += self.module_time[m][self.batch_num]

        # estimated time budget for each remaining batch
        time_left = data_info['time_budget'] - (self.predict_start_time - time_info[1])
        batch_left = self.total_batch_num - self.batch_num
        time_per_batch = time_left / batch_left

        # estimated time budget for training models
        ratio = float(self.batch_window_size + 1) / (min(self.batch_num, self.batch_window_size) + 1)
        time_for_model = time_per_batch - time_spent_invariant - time_spent_variant * ratio

        clf = GBM(category_cols=None, hyper_tune=False, hyper_param=self.hyper_param)

        if (self.batch_num == 1):
            # decide boosting iterations
            self.suggested_boost_round = self.time_manager.suggest_boosting_round(feature_new, label, time_for_model, self.batch_num)
            self.suggested_learning_rate, self.early_stop_round = clf.suggest_learning_rate(feature_new, label, self.suggested_boost_round)
            self.num_boost_round = self.early_stop_round
            print ('max boost round suggested by time manager: %d' %(self.suggested_boost_round))
            print ('early stop round: %d' %(self.early_stop_round))
            print ('suggested learning rate: %f' %(self.suggested_learning_rate))
        else:
            # adjust boosting round number according to time budget
            ratio = min(self.batch_window_size, self.batch_num) / (min(self.batch_window_size, self.batch_num) - 1)
            estimated_train_time = self.module_time['fit'][self.batch_num - 1] * ratio
            remaining_time = time_for_model - estimated_train_time
            if (remaining_time > time_for_model * 0.1 and self.num_boost_round < 2000):
                self.num_boost_round += 200
            elif (remaining_time < 0.):
                self.num_boost_round -= 100

        print ('training info of batch %d: ' %(self.batch_num))
        print ('feature number: %d' %(feature_new.shape[1]))
        print ('train sample size: %d' %(feature_new.shape[0]))
        print ('test sample size: %d' %(self.X_new.shape[0]))
        print ('num boost round: %d' %(self.num_boost_round))
        print ('learning rate: %f' %(self.suggested_learning_rate))

        # LGB
        module_start = time.time()

        updated_params = {'n_estimators': self.num_boost_round, 'learning_rate': self.suggested_learning_rate}
        self.hyper_param = clf.fit(feature_new, label, weight=weight, updated_params=updated_params)

        module_end = time.time()
        self.module_time['fit'][self.batch_num] = module_end - module_start

        module_start = time.time()

        y_pred = clf.predict(self.X_new)

        module_end = time.time()
        self.module_time['predict'][self.batch_num] = module_end - module_start

        self.time_spent_ratio = (time.time() - self.predict_start_time) / time_per_batch * 100.

        self.is_trained = True

        return y_pred

    def predict(self, F, data_info, time_info):
        '''
        This function should provide predictions of labels on (test) data.
        Here we just return random values...
        Make sure that the predicted values are in the correct format for the scoring
        metric. For example, binary classification problems often expect predictions
        in the form of a discriminant value (if the area under the ROC curve it the metric)
        rather that predictions of the class labels themselves.
        The function predict eventually can return probabilities or continuous values.
        '''
        self.batch_num += 1
        self.predict_start_time = time.time()
        self.module_time['read_data'][self.batch_num] = (self.predict_start_time - self.fit_end_time)

        module_start = time.time()

        F['numerical'] = self.missing_value_preprocess.transform(F['numerical'], 'numerical', input_type='ndarray')
        F['CAT'] = self.missing_value_preprocess.transform(F['CAT'], 'CAT', input_type='dataframe')
        if self.use_mv:
            F['MV'] = self.missing_value_preprocess.transform(F['MV'], 'MV', input_type='dataframe')

        F['numerical'] = data_converter.replace_missing(F['numerical']).astype('float32')
        F['CAT'] = F['CAT'].fillna('-1')

        module_end = time.time()
        self.module_time['preprocess'][self.batch_num] = module_end - module_start

        self.F = F

        self.y_pred = self.transferPredict(0, data_info, time_info)

        self.predict_end_time = time.time()
        return self.y_pred

    def save(self, path="./"):
        pickle.dump(self, open(path + '_model.pickle', "w"))

    def load(self, path="./"):
        modelfile = path + '_model.pickle'
        if isfile(modelfile):
            with open(modelfile) as f:
                self = pickle.load(f)
            print("Model reloaded from: " + modelfile)
        return self


# Project) 오락실 Pang 게임 만들기

# [게임 조건]
# 1. 캐릭터는 화면 아래에 위치, 좌우로만 이동 가능
# 2. 스페이스를 누르면 무기를 쏘아 올림
# 3. 큰 공 1 개가 나타나서 바운스
# 4. 무기에 닿으면 공은 작은 크기 2 개로 분할, 가장 작은 크기의 공은 사라짐
# 5. 모든 공을 없애면 게임 종료 => 성공
# 6. 캐릭터는 공에 닿으면 게임 종료 => 실패
# 7. 시간 제한 99 초 초과 시 게임 종료 => 실패
# 8. FPS 는 30 으로 고정 => 필요 시 speed 값을 조정

# [게임 이미지]
# 1. 배경 : 640 * 480 (가로 세로) - background.png
# 2. 무대 : 640 * 50 - stage.png
# 3. 캐릭터 : 60 * 33 - character.png
# 4. 무기 : 20 * 430 - weapon.png
# 5. 공 : 160 * 160, 80 * 80, 40 * 40, 20 * 20 - balloon1.png ~ balloon4.png
######################################################
import os
import pygame
######################################################
# 기본 초기화 (반드시 해야 하는 것들)
pygame.init()

# 화면 크기 설정
screen_width = 640  # 가로 크기
screen_height = 480 # 세로 크기
screen = pygame.display.set_mode((screen_width, screen_height))

# 화면 타이틀 설정
pygame.display.set_caption("*** Pang pang ***")

# FPS 초당 프레임 수
clock = pygame.time.Clock()
######################################################

# 1. 사용자 게임 초기화 (배경 화면, 게임 이미지, 좌표, 속도, 폰트 등)
current_path = os.path.dirname(__file__)    # 현재 파일의 위치 반환
image_path = os.path.join(current_path, "images")   # 현재 폴더 위치에 images 폴더 위치를 더해 이미지가 있는 위치 반환

# 배경화면 만들기
background = pygame.image.load(os.path.join(image_path, "background.png"))  # image_path 경로의 background.png 파일을 불러오기

# 스테이지 만들기
stage = pygame.image.load(os.path.join(image_path, "stage.png"))
stage_size = stage.get_rect().size
stage_height = stage_size[1]    # 스테이지의 높이 위에 캐릭터를 두기 위해 사용

# 캐릭터 만들기
character = pygame.image.load(os.path.join(image_path, "character.png"))
character_size = character.get_rect().size
character_width = character_size[0]
character_height = character_size[1]
character_x_pos = (screen_width / 2) - (character_width / 2)
character_y_pos = screen_height - character_height - stage_height

running = True
while running:
    dt = clock.tick(30)

    # 2. 이벤트 처리 (키보드, 마우스 등)
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    # 3. 게임 캐릭터 위치 정의

    # 4. 충돌 처리

    # 5. 화면에 그리기
    screen.blit(background, (0, 0))
    screen.blit(stage, (0, screen_height - stage_height))
    screen.blit(character, (character_x_pos, character_y_pos))

    pygame.display.update()

pygame.quit()


import ipywidgets as widgets

def make_model_list_dropdown() -> widgets.Widget:
    model_list_dropdown = widgets.Dropdown(
        options=['Model 1', "Click to add new model..."],
        value='Model 1',
        description='Select Model',
        style={
            'description_width': '100px'
        },
        layout={
            'width': '400px'
        }
    )

    return model_list_dropdown

def make_model_list_box() -> widgets.Widget:
    model_list_box = widgets.HBox(
        children=[make_model_list_dropdown()],
        layout={}
    )

    return model_list_box

def make_clear_button() -> widgets.Widget:
    clear_button = widgets.Button(
        description='Clear All Plots',
        icon='chart-line',
        style={
            'button_color': '#9ecae1'
        },
        layout={'width': '400px'},
        tooltip="Removes all traces from the small-angle scattering and dark field plots on the right."
    )

    return clear_button

def make_export_button() -> widgets.Widget:
    export_button = widgets.Button(
        description='Export Plot Data',
        icon='save',
        style={
            'button_color': '#4292c6'
        },
        layout={'width': '400px'},
        tooltip="Exports SAS and dark field data from the plots to csv files in the working directory."
    )

    return export_button

class ModelHeaderBox:

    def __init__(self):
        model_list = make_model_list_box()

        clear_button = make_clear_button()
        # clear_button.on_click(clear_plots_func)

        export_button = make_export_button()
        # export_button.on_click(export_data_func)

        self.model_header_box = widgets.VBox(
            children=[
                model_list,
                widgets.HBox(children=[clear_button, export_button])
            ]
        )


import math

def factors(num):
    rev_lst, lst = [], []
    num = int(num)
    for i in range(1, math.ceil(math.sqrt(num))):
        if num % i == 0:
            if num / i == i:
                lst.append(i)
            else:
                lst.append(i)
                rev_lst.append(int(num / i))
    final_lst = lst + rev_lst[::-1]
    for j in final_lst:
        yield j

def main():
    for h in factors(1000):
        print(h)


import re
import sys
from io import StringIO

from colorama import Fore, Style
from diff_match_patch import diff_match_patch

def highlight_differences(text1, text2):
    dmp = diff_match_patch()
    diffs = dmp.diff_main(text1, text2)
    dmp.diff_cleanupSemantic(diffs)

    highlighted_diff = ""
    for diff in diffs:
        if diff[0] == -1:
            highlighted_diff += Fore.RED + diff[1] + Style.RESET_ALL
        elif diff[0] == 1:
            highlighted_diff += Fore.GREEN + diff[1] + Style.RESET_ALL
        else:
            highlighted_diff += diff[1]

    return highlighted_diff

def capture_output(func, *args, **kwargs) -> str:
    original_stdout = sys.stdout
    sys.stdout = StringIO()

    func(*args, **kwargs)

    output_value = sys.stdout.getvalue()

    sys.stdout = original_stdout

    return output_value

def compare_output(output_value, expected_output_file) -> bool:
    with open(expected_output_file, 'r') as f:
        expected_output = f.read().strip()

    output_value = output_value.strip()

    output_value = re.sub(r"ic\|.*\n", "", output_value)

    if output_value != expected_output:
        print("Output does not match the expected output.")
        print()
        highlighted_diff = highlight_differences(expected_output, output_value)
        print(highlighted_diff)
        print()

    return output_value == expected_output


#!/usr/bin/env python3

import os
import datetime

# Complete the time_delta function below.
def time_delta(t1, t2):
    pattern = '%a %d %b %Y %H:%M:%S %z'
    date1 = int(datetime.datetime.strptime(t1, pattern).timestamp())
    date2 = int(datetime.datetime.strptime(t2, pattern).timestamp())

    print(abs(date1 - date2))
    return abs(date1 - date2)

if __name__ == '__main__':
    fptr = open(os.environ['OUTPUT_PATH'], 'w')

    t = int(input())

    for t_itr in range(t):
        t1 = input()

        t2 = input()

        delta = time_delta(t1, t2)

        fptr.write(str(delta) + '\n')

    fptr.close()


from tqdm import tqdm

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

from group4_banker import Group4Banker

from plot_config import setup, set_fig_size, set_arrowed_spines

setup()

def prep_data():

    features = ['checking account balance', 'duration', 'credit history',
                'purpose', 'amount', 'savings', 'employment', 'installment',
                'marital status', 'other debtors', 'residence time',
                'property', 'age', 'other installments', 'housing', 'credits',
                'job', 'persons', 'phone', 'foreign']

    target = 'repaid'

    df_train = pd.read_csv("../../data/credit/D_train.csv", sep=' ', names=features+[target])
    df_test = pd.read_csv("../../data/credit/D_test.csv", sep=' ', names=features+[target])

    numerical_features = ['duration', 'age', 'residence time', 'installment', 'amount', 'persons', 'credits']
    quantitative_features = list(filter(lambda x: x not in numerical_features, features))
    D_train = pd.get_dummies(df_train, columns=quantitative_features, drop_first=True)
    D_test = pd.get_dummies(df_test, columns=quantitative_features, drop_first=True)
    encoded_features = list(filter(lambda x: x != target, D_train.columns))

    return D_train, D_test, encoded_features, target

def oob_sampling(model, X, y, n_samples, size_sample, seed):

    np.random.seed(seed)

    Y_true = np.zeros((n_samples, size_sample))
    Y_pred = np.zeros((n_samples, size_sample))

    for i in tqdm(range(n_samples)):

        test_idx = np.random.choice(np.arange(X.shape[0]), replace=True, size=size_sample)

        y_test = y.iloc[test_idx]
        X_test = X.iloc[test_idx]

        Y_true[i] = y_test.values - 1
        Y_pred[i] = [int(model.get_best_action(x)) for _, x in X_test.iterrows()]

    return Y_true, Y_pred

def posterior_hypothesis(X, p):

    n_samples, n_trails = np.shape(X)

    posterior = np.zeros_like(X, dtype=float)
    for i in range(n_trails):

        # Parameters for Beta prior.
        alpha = 1
        beta = 1

        log_p, log_p_marginal = 0, 0
        for j in range(n_samples):

            x = X[j, i]
            p_x = (x * alpha + (1 - x) * beta) / (alpha + beta)

            alpha = alpha + x
            beta = beta + 1 - x

            # Prior for the null hypothesis being true.
            log_p = log_p + np.log(p)
            log_p_marginal = log_p_marginal + np.log(p_x)

            posterior[j, i] = np.exp(log_p - np.log(np.exp(log_p_marginal) + np.exp(log_p)))

    return posterior

def plot_posteriors(Y_true, Y_pred):

    delta = 0.05

    # H0:
    p_not_grant = 1 - np.mean(Y_true)
    p_not_grant_low = p_not_grant - np.sqrt(np.log(2) * delta / (2 * Y_true.shape[0]))
    p_not_grant_high = p_not_grant + np.sqrt(np.log(2) * delta / (2 * Y_true.shape[0]))

    # Posterior hypothesis of H0.
    post_grant_true = posterior_hypothesis(Y_true, p=p_not_grant)
    post_grant_pred = posterior_hypothesis(Y_pred, p=p_not_grant)
    post_grant_pred_low = posterior_hypothesis(Y_pred, p=p_not_grant_low)
    post_grant_pred_high = posterior_hypothesis(Y_pred, p=p_not_grant_high)

    fig, ax = plt.subplots(1, 1, figsize=set_fig_size(500, fraction=1))
    ax.set_title(r"Posterior probability of accepting $H_0$")
    ax.plot(np.mean(post_grant_true, axis=1), label="Test data", c="darkorange", alpha=0.7)
    ax.plot(np.mean(post_grant_pred, axis=1), label="Model predictions", alpha=0.7)
    ax.fill_between(np.arange(Y_true.shape[0]),
                    np.mean(post_grant_pred_low, axis=1),
                    np.mean(post_grant_pred_high, axis=1), alpha=0.2)
    ax.legend()
    ax.set_ylabel("Posterior probability")
    ax.set_xlabel("Number of bootstrap samples")
    set_arrowed_spines(fig, ax)
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)
    plt.tight_layout()
    fig.savefig("posterior_null_hypo.pdf")

    bayes_reject_grant_true = np.mean(post_grant_true < p_not_grant, axis=1)
    bayes_reject_grant_pred = np.mean(post_grant_pred < p_not_grant, axis=1)
    bayes_reject_grant_pred_low = np.mean(post_grant_pred_low < p_not_grant, axis=1)
    bayes_reject_grant_pred_high = np.mean(post_grant_pred_high < p_not_grant, axis=1)

    fig, ax = plt.subplots(1, 1, figsize=set_fig_size(500, fraction=1))
    ax.set_title(r"Posterior probability of rejecting $H_0$")
    ax.plot(bayes_reject_grant_true, label="Test data", c="darkorange", alpha=0.7)
    ax.plot(bayes_reject_grant_pred, label="Model predictions", alpha=0.7)
    ax.fill_between(np.arange(Y_true.shape[0]),
                    bayes_reject_grant_pred_low,
                    bayes_reject_grant_pred_high, alpha=0.2)
    ax.legend()
    ax.set_ylabel("Posterior probability")
    ax.set_xlabel("Number of bootstrap samples")
    set_arrowed_spines(fig, ax)
    ax.spines['top'].set_visible(False)
    ax.spines['right'].set_visible(False)
    plt.tight_layout()
    fig.savefig("reject_null_hypo.pdf")

def main():

    D_train, D_test, encoded_features, target = prep_data()

    X_train = D_train.loc[:, encoded_features]
    y_train = D_train.loc[:, target]

    model = Group4Banker(optimize=False, random_state=42)
    model.set_interest_rate(0.05)
    model.fit(X_train, y_train)

    X_test = D_test.loc[:, encoded_features]
    y_test = D_test.loc[:, target]

    Y_true, Y_pred = oob_sampling(model, X_test, y_test, 500, 50, 42)

    np.save("Y_true.npy", Y_true)
    np.save("Y_pred.npy", Y_pred)

    Y_true = np.load("Y_true.npy")
    Y_pred = np.load("Y_pred.npy")

    plot_posteriors(Y_true, Y_pred)

if __name__ == "__main__":
    main()


from django.urls import path
from . import views

urlpatterns = [
        path('', views.index, name='index'),
        path('event/', views.event, name='event'),
        path('place/', views.place, name='place'),
        path('imply/', views.imply, name='imply'),
        path('insert_event/', views.insert_event, name='insert_event'),
        path('insert_place/', views.insert_place, name='insert_place'),
        path('insert_imply/', views.insert_imply, name='insert_imply'),
        path('insert_picture/', views.insert_picture, name='insert_picture'),
        path('participate_event/', views.participate_event, name='participate_event'),
        path("insert_user/", views.insert_user, name="insert_user"),
        path("insert_user_direct/", views.insert_user_direct, name="insert_user_direct"),
        path("id_duplicate_check/", views.id_duplicate_check, name="id_duplicate_check"),
        path("login/", views.login, name="login"),
        path("auth/", views.auth, name="auth"),
        path("ranking/", views.ranking, name="ranking"),
        path("participating/", views.participating, name="participating"),
        path('select_auth_method/', views.select_auth_method, name='select_auth_method'),
        path('finish_auth_check/', views.finish_auth_check, name='finish_auth_check'),
        path('place_auth_info/', views.place_auth_info, name='place_auth_info'),
]