import nltk
import os
import inspect

def get_evaluation(n_test=100):
    current_dir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
    lines = open( os.path.join(current_dir, "evaluation.txt")).readlines()
    data = []

    import nltk

    for line in lines:
        indexes = []
        tokenize_line = nltk.word_tokenize(line)
        misspelled_line = tokenize_line.copy()
        corrected_line  = tokenize_line.copy()
        for index,word in enumerate(tokenize_line):
            if '|' in word:
                misspelled_line[index] = word.split('|')[0]
                corrected_line[index]  = word.split('|')[1]
                indexes.append(index)
        data.append({'original':misspelled_line,'corrected':corrected_line,'indexes':indexes})

    test = data[:n_test]
    train = data[n_test:]
    return train, test


import numpy as np
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.metrics import mean_squared_error, mean_absolute_error, r2_score
import logging

logger = logging.getLogger(__name__)

class RegresionModelBase:
    """
    Clase base para modelos de regresión.

    Parameters:
        data (pandas.DataFrame): Conjunto de datos que contiene las variables independientes y dependientes.
    """

    def __init__(self, data):
        """
        Inicializa la instancia del modelo de regresión.

        Parameters:
            data (pandas.DataFrame): Conjunto de datos que contiene las variables independientes y dependientes.

        Raises:
            ValueError: Se lanza si el conjunto de datos no contiene las columnas esperadas.
        """
        expected_columns = ['MinTemp', 'MaxTemp', 'Evaporation', 'Cloud9am', 'Cloud3pm', 'Temp9am', 'Temp3pm',
                            'RainToday', 'RainfallTomorrow']

        if not set(expected_columns).issubset(data.columns):
            raise ValueError(f"El conjunto de datos debe contener las columnas: {expected_columns}")

        self.data = data
        self.model = LinearRegression()
        self.features = expected_columns

    def regresion(self):
        """
        Entrena el modelo de regresión, realiza predicciones en el conjunto de prueba y muestra métricas de rendimiento.

        Returns:
            tuple: Una tupla que contiene x_test, y_test, y_pred y el modelo entrenado.

        Raises:
            ValueError: Se lanza si hay problemas durante la regresión.
        """
        try:
            X = self.data[self.features]
            y = self.data['Rainfall']

            # Dividir los datos en conjuntos de entrenamiento y prueba
            X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)

            # Definir los hiperparámetros a ajustar con Grid Search
            param_grid = {'fit_intercept': [True, False], 'positive': [True, False]}

            # Crear el objeto GridSearchCV
            grid_search = GridSearchCV(estimator=self.model, param_grid=param_grid, scoring='neg_mean_squared_error',
                                       cv=5)

            # Entrenar el modelo con búsqueda de hiperparámetros
            grid_search.fit(X_train, y_train)

            # Obtener el mejor modelo después de la búsqueda de hiperparámetros
            best_model = grid_search.best_estimator_

            # Realizar predicciones en el conjunto de prueba con el mejor modelo
            predictions = best_model.predict(X_test)
            mse = mean_squared_error(y_test, predictions)
            mae = mean_absolute_error(y_test, predictions)
            rmse = np.sqrt(mse)
            r2 = r2_score(y_test, predictions)

            # Mostrar las métricas utilizando el logger o la función print
            print(f"Error Cuadrático Medio en el conjunto de prueba: {mse}")
            print(f"Error Absoluto Medio en el conjunto de prueba: {mae}")
            print(f"Raíz del Error Cuadrático Medio en el conjunto de prueba: {rmse}")
            print(f"Coeficiente de Determinación (R^2) en el conjunto de prueba: {r2}")
            # Devolver resultados en el formato esperado
            return X_test, y_test, predictions, best_model

        except Exception as e:
            logger.error(f"Error durante la regresión: {e}")
            raise ValueError("Error durante la regresión. Consulta los registros para obtener más detalles.") from e


#!/usr/bin/python3
"""sends a POST request to a URL with letter as parameter"""

import requests
from sys import argv

if __name__ == "__main__":
    url = "http://0.0.0.0:5000/search_user"
    arg_val = argv[1] if len(argv) > 1 else ""
    the_data = {"q": arg_val}

    try:
        r = requests.post(url, data=the_data)
        if r.json():
            print(f"[{r.json()['id']}] {r.json()['name']}")
        else:
            print("No result")
    except ValueError:
        print("Not a valid JSON")


from django.conf.urls import url

from ralph.dc_view.views.ui import ServerRoomView, SettingsForAngularView

urlpatterns = [
    url(
        r'^dc_view/?$',
        ServerRoomView.as_view(),
        name='dc_view'
    ),
    url(
        r'^settings.js$', SettingsForAngularView.as_view(), name='settings-js',
    ),
]


array = [9,9,9,9]

stringArray = "".join(str(number) for number in array)

number = int(stringArray,10)

number += 1

result = str(number)
finalResult = []

for i in range(len(result)):
    num = int(result[i],10)
    finalResult.append(num)

print(finalResult)

print(array)


from enum import Enum
from PIL import ImageColor

class Color(Enum):
    RED = {"id": 2, "hex": "#FF4500"}
    ORANGE = {"id": 3, "hex": "#FFA800"}
    YELLOW = {"id": 4, "hex": "#FFD635"}
    DARK_GREEN = {"id": 6, "hex": "#00A368"}
    LIGHT_GREEN = {"id": 8, "hex": "#7EED56"}
    DARK_BLUE = {"id": 12, "hex": "#2450A4"}
    BLUE = {"id": 13, "hex": "#3690EA"}
    CYAN = {"id": 14, "hex": "#51E9F4"}
    DARK_PURPLE = {"id": 18, "hex": "#811E9F"}
    PURPLE = {"id": 19, "hex": "#B44AC0"}
    PINK = {"id": 23, "hex": "#FF99AA"}
    BROWN = {"id": 25, "hex": "#9C6926"}
    BLACK = {"id": 27, "hex": "#000000"}
    GREY = {"id": 29, "hex": "#898D90"}
    LIGHT_GREY = {"id": 30, "hex": "#D4D7D9"}
    WHITE = {"id": 31, "hex": "#FFFFFF"}

# generate rgb values for all colors
for color in Color:
    color.value["rgb"] = ImageColor.getcolor(color.value["hex"], "RGB")

def get_matching_color(rgb) -> Color:
    for color in Color:
        if color.value["rgb"] == rgb:
            return color
    return None

def get_color_from_index(index) -> Color:
    for color in Color:
        if color.value["id"] == index:
            return color
    return None

# def get_closest_color(r, g, b) -> Color: # This function was written in its entirety by GPT3, WTF
#     min_distance = None
#     closest_color = None
#     for color in Color:
#         distance = (r - color.value["rgb"][0]) ** 2 + (g - color.value["rgb"][1]) ** 2 + (b - color.value["rgb"][2]) ** 2
#         if min_distance is None or distance < min_distance:
#             min_distance = distance
#             closest_color = color
#     return closest_color

def get_closest_color(r, g, b) -> Color:
    return min(list(Color), key=lambda color: (r - color.value["rgb"][0]) ** 2 + (g - color.value["rgb"][1]) ** 2 + (b - color.value["rgb"][2]) ** 2)


from turtle import forward
import torch
import torch.nn as nn
import torch.nn.functional as F
import numpy as np

# 3D最大池化
class MaxPool3dSamePadding(nn.MaxPool3d):

    # 计算各个维度的padding
    def compute_pad(self, dimension, s_channel):
        if s_channel % self.stride[dimension] == 0:
            return max(self.kernel_size[dimension] - self.stride[dimension], 0)
        else:
            return max(self.kernel_size[dimension] - (s_channel % self.stride[dimension]), 0)

    def forward(self, x):
        # 计算不同维度的padding
        (batch, channel, time, height, width) = x.size()
        # 计算大于等于该值的最小整数
        out_time = np.ceil(float(time) / float(self.stride[0]))
        out_height = np.ceil(float(height) / float(self.stride[1]))
        out_width = np.ceil(float(width) / float(self.stride[2]))

        pad_time = self.compute_pad(0, time)
        pad_height = self.compute_pad(1, height)
        pad_width = self.compute_pad(2, width)

        # 将pad_time，pad_height，pad_width平分后扩充到相应维度
        # 令pad_** = pad_**_f + pad_**_b
        pad_time_f = pad_time // 2
        pad_time_b = pad_time - pad_time_f
        pad_height_f = pad_height // 2
        pad_height_b = pad_height - pad_height_f
        pad_width_f = pad_width // 2
        pad_width_b = pad_width - pad_width_f

        # 将所有需要扩充的维度合并为pad
        pad = (pad_width_f, pad_width_b, pad_height_f, pad_height_b, pad_time_f, pad_time_b)
        x = F.pad(x, pad)
        # 返回父类的方法
        return super(MaxPool3dSamePadding, self).forward(x)

# 3D单元
class Unit3D(nn.Module):

    def __init__(self,
            in_channels,
            out_channels,
            kernel_size=(1, 1, 1),
            stride=(1, 1, 1),
            padding=0,
            activation_fn=F.relu,
            use_batch_norm=True,
            use_bias=False,
            name="unit_3d"
            ):

        "初始化Unit3D模型"
        super(Unit3D, self).__init__()

        self._out_channels = out_channels
        self._kernel_size = kernel_size
        self._stride = stride
        self.padding = padding
        self._activation_fn = activation_fn
        self._use_batch_norm = use_batch_norm
        self._use_bias = use_bias
        self.name = name

        # 搭建3D CNN
        self.conv3d = nn.Conv3d(in_channels=in_channels,
                                out_channels=self._out_channels,
                                kernel_size=self._kernel_size,
                                stride=self._stride,
                                padding=0,# 我们将根据输入大小在forward函数中动态pad
                                bias=self._use_bias
                                )
        # 是否使用BN
        if self._use_batch_norm:
            self.bn = nn.BatchNorm3d(self._out_channels, eps=0.001, momentum=0.01)

    # 计算各个维度的padding
    def compute_pad(self, dimension, s_channel):
        if s_channel % self._stride[dimension] == 0:
            return max(self._kernel_size[dimension] - self._stride[dimension], 0)
        else:
            return max(self._kernel_size[dimension] - (s_channel % self._stride[dimension]), 0)

    def forward(self, x):
        # 计算不同维度的padding
        (batch, channel, time, height, width) = x.size()
        # 计算大于等于该值的最小整数
        out_time = np.ceil(float(time) / float(self._stride[0]))
        out_height = np.ceil(float(height) / float(self._stride[1]))
        out_width = np.ceil(float(width) / float(self._stride[2]))

        pad_time = self.compute_pad(0, time)
        pad_height = self.compute_pad(1, height)
        pad_width = self.compute_pad(2, width)

        # 将pad_time，pad_height，pad_width平分后扩充到相应维度
        # 令pad_** = pad_**_f + pad_**_b
        pad_time_f = pad_time // 2
        pad_time_b = pad_time - pad_time_f
        pad_height_f = pad_height // 2
        pad_height_b = pad_height - pad_height_f
        pad_width_f = pad_width // 2
        pad_width_b = pad_width - pad_width_f

        # 将所有需要扩充的维度合并为pad
        pad = (pad_width_f, pad_width_b, pad_height_f, pad_height_b, pad_time_f, pad_time_b)
        x = F.pad(x, pad)

        x = self.conv3d(x)
        # 是否加入BN
        if self._use_batch_norm:
            x = self.bn(x)
        # 是否加入激活函数
        if self._activation_fn is not None:
            x = self._activation_fn(x)
        return x

# 元模块
class InceptionModule(nn.Module):
    def __init__(self, in_channels, out_channels, name):
        super(InceptionModule, self).__init__()

        # branch0结构
        self.b0 = Unit3D(in_channels=in_channels, out_channels=out_channels[0],
                        kernel_size=[1, 1, 1], padding=0,
                        name=name+'/Branch_0/Conv3d_0a_1x1'
                        )
        # branch1结构
        self.b1a = Unit3D(in_channels=in_channels, out_channels=out_channels[1],
                        kernel_size=[1, 1, 1], padding=0,
                        name=name+'/Branch_1/Conv3d_0a_1x1'
                        )
        self.b1b = Unit3D(in_channels=out_channels[1], out_channels=out_channels[2],
                        kernel_size=[3, 3, 3],
                        name=name+'/Branch_1/Conv3d_0b_3x3'
                        )
        # branch2结构
        self.b2a = Unit3D(in_channels=in_channels, out_channels=out_channels[3],
                        kernel_size=[1, 1, 1], padding=0,
                        name=name+'/Branch_2/Conv3d_0a_1x1'
                        )
        self.b2b = Unit3D(in_channels=out_channels[3], out_channels=out_channels[4],
                        kernel_size=[3, 3, 3],
                        name=name+'/Branch_2/Conv3d_0b_3x3'
                        )
        # branch3结构
        self.b3a = MaxPool3dSamePadding(kernel_size=[3, 3, 3],stride=(1, 1, 1), padding=0)
        self.b3b = Unit3D(in_channels=in_channels, out_channels=out_channels[5],
                        kernel_size=[1, 1, 1], padding=0,
                        name=name+'/Branch_3/Conv3d_0b_1x1'
                        )
        self.name = name

    def forward(self, x):
        branch0 = self.b0(x)
        branch1 = self.b1b(self.b1a(x))
        branch2 = self.b2b(self.b2a(x))
        branch3 = self.b3b(self.b3a(x))
        return torch.cat([branch0,branch1,branch2,branch3], dim=1)

# I3D元模块
class InceptionI3d(nn.Module):
    '''Inception-v1 I3D结构'''

    # 按顺序排列模型的端点
    # 过程中，所有端点在达到'final_endpoint'后都在字典中作为第二个返回值返回。
    # 结构列表
    VALID_ENDPOINTS = (
        'Conv3d_1a_7x7',    #1
        'MaxPool3d_2a_3x3', #2
        'Conv3d_2b_1x1',
        'Conv3d_2c_3x3',
        'MaxPool3d_3a_3x3', #3
        'Mixed_3b',
        'Mixed_3c',
        'MaxPool3d_4a_3x3', #4
        'Mixed_4b',
        'Mixed_4c',
        'Mixed_4d',
        'Mixed_4e',
        'Mixed_4f',
        'MaxPool3d_5a_2x2', #5
        'Mixed_5b',
        'Mixed_5c',
        'Logits',           # full—linear
        'Predecitions',     # prediction
    )
    def __init__(self, num_classes=400, spatial_squeeze=True,
                final_endpoint='Logits', name='inception_i3d',
                in_channels=3, dropout_keep_prob=0.5
                ):
        '''初始化I3D模型实例：
        Args：
            num_classes:输出层的输出分类数
            spatial_squeeze:是否在返回之前压缩logit的空间维度
            final_endpoint：指定要构建的模型的最后一个端点，final_endpoint之前所有端点输出也会在return时返回
            name:该模块的名字
        Raises:
          ValueError: if `final_endpoint` is not recognized.
        '''

        # 如果final_endpoint没有出现在列表中
        if final_endpoint not in self.VALID_ENDPOINTS:
            raise ValueError('Unknown final endpoint %s' % final_endpoint)

        # 初始化
        super(InceptionI3d, self).__init__()
        self._num_classes = num_classes
        self._spatial_squeeze = spatial_squeeze
        self._final_endpoint = final_endpoint
        self.logits = None

        if self._final_endpoint not in self.VALID_ENDPOINTS:
            raise ValueError('Unknown final endpoint %s' % self._final_endpoint)

        # 构建网络蓝图
        self.end_points = {}

        end_point = 'Conv3d_1a_7x7'
        self.end_points[end_point] = Unit3D(in_channels=in_channels,
                                            out_channels=64,
                                            kernel_size=[7, 7, 7],
                                            stride=(2, 2, 2),
                                            padding=(3, 3, 3),
                                            name=name+end_point)
        if self._final_endpoint == end_point:return

        end_point = 'MaxPool3d_2a_3x3'
        self.end_points[end_point] = MaxPool3dSamePadding(
                                            kernel_size=[1, 3, 3],
                                            stride=(1, 2, 2),
                                            padding=0)
        if self._final_endpoint == end_point:return

        end_point = 'Conv3d_2b_1x1'
        self.end_points[end_point] = Unit3D(in_channels=64,
                                            out_channels=64,
                                            kernel_size=[1, 1, 1],
                                            padding=0,
                                            name=name+end_point)
        if self._final_endpoint == end_point:return

        end_point = 'Conv3d_2c_3x3'
        self.end_points[end_point] = Unit3D(in_channels=64,
                                            out_channels=192,
                                            kernel_size=[3, 3, 3],
                                            padding=1,
                                            name=name+end_point)
        if self._final_endpoint == end_point:return

        end_point = 'MaxPool3d_3a_3x3'
        self.end_points[end_point] = MaxPool3dSamePadding(
                                            kernel_size=[1, 3, 3],
                                            stride=(1, 2, 2),
                                            padding=0)
        if self._final_endpoint == end_point:return

        end_point = 'Mixed_3b'
        self.end_points[end_point] = InceptionModule(192,[64,96,128,16,32,32], name+end_point)
        if self._final_endpoint == end_point:return
        # in_channels = 上一级Inc的out_channels中[0]+[2]+[4]+[5]
        end_point = 'Mixed_3c'
        self.end_points[end_point] = InceptionModule(64+128+32+32,[128,128,192,32,96,64], name+end_point)
        if self._final_endpoint == end_point:return

        end_point = 'MaxPool3d_4a_3x3'
        self.end_points[end_point] = MaxPool3dSamePadding(
                                            kernel_size=[3, 3, 3],
                                            stride=(2, 2, 2),
                                            padding=0)
        if self._final_endpoint == end_point:return

        end_point = 'Mixed_4b'
        self.end_points[end_point] = InceptionModule(128+192+96+64,[192,96,208,16,48,64], name+end_point)
        if self._final_endpoint == end_point:return

        end_point = 'Mixed_4c'
        self.end_points[end_point] = InceptionModule(192+208+48+64,[160,112,224,24,64,64], name+end_point)
        if self._final_endpoint == end_point:return

        end_point = 'Mixed_4d'
        self.end_points[end_point] = InceptionModule(160+224+64+64,[128,128,256,24,64,64], name+end_point)
        if self._final_endpoint == end_point:return

        end_point = 'Mixed_4e'
        self.end_points[end_point] = InceptionModule(128+256+64+64,[112,144,288,32,64,64], name+end_point)
        if self._final_endpoint == end_point:return

        end_point = 'Mixed_4f'
        self.end_points[end_point] = InceptionModule(112+288+64+64,[256,160,320,32,128,128], name+end_point)
        if self._final_endpoint == end_point:return

        end_point = 'MaxPool3d_5a_2x2'
        self.end_points[end_point] = MaxPool3dSamePadding(kernel_size=[2, 2, 2], stride=(2, 2, 2),
                                                            padding=0)
        if self._final_endpoint == end_point:return

        end_point = 'Mixed_5b'
        self.end_points[end_point] = InceptionModule(256+320+128+128, [256,160,320,32,128,128], name+end_point)
        if self._final_endpoint == end_point: return

        end_point = 'Mixed_5c'
        self.end_points[end_point] = InceptionModule(256+320+128+128, [384,192,384,48,128,128], name+end_point)
        if self._final_endpoint == end_point: return

        end_point = 'Logits'
        self.avg_pool = nn.AvgPool3d(kernel_size=[2, 7, 7], stride=(1, 1, 1))
        self.dropout = nn.Dropout(dropout_keep_prob)
        self.logits = Unit3D(in_channels=384+384+128+128, out_channels=self._num_classes,
                            kernel_size=[1, 1, 1], padding=0, activation_fn=None,
                            use_batch_norm=False, use_bias=True, name='logits')

        # 搭建指令
        self.build()

    # 将模型结构依次添加创建网络结构
    def build(self):
        for key in self.end_points.keys():
            self.add_module(key, self.end_points[key])

    def forward(self, x, no_logits=False):
        for end_point in self.VALID_ENDPOINTS:
            if end_point in self.end_points:
                # 使用modules
                x = self._modules[end_point](x)

        # Logits作为最后一层
        if self._final_endpoint == 'Logits' and no_logits is False:
            x = self.logits(self.dropout(self.avg_pool(x)))
            # 将空间维度压缩
            if self._spatial_squeeze:
                x = x.squeeze(3).squeeze(3)
                # logits输出为(batch, classes, time)
        return x

    # 更替最后的logits层
    def replace_logits(self, num_classes):
        self._num_classes = num_classes
        self.logits = Unit3D(in_channels=384+384+128+128, out_channels=self._num_classes,
                             kernel_size=[1, 1, 1],
                             padding=0,
                             activation_fn=None,
                             use_batch_norm=False,
                             use_bias=True,
                             name='logits')

    # def extract_features(self, x):
    #     for end_point in self.VALID_ENDPOINTS:
    #         if end_point in self.end_points:
    #             x = self._modules[end_point](x)
    #     return self.avg_pool(x)

    # def extract_conv_output(self, x):
    #     for end_point in self.VALID_ENDPOINTS:
    #         if end_point in self.end_points:
    #             x = self._modules[end_point](x)
    #     return x

    # def conv_output_to_model_output(self, x):
    #     x = self.logits(self.dropout(self.avg_pool(x)))
    #     if self._spatial_squeeze:
    #         logits = x.squeeze(3).squeeze(3)
    #     # logits is batch X time X classes, which is what we want to work with
    #     return logits


import json
import os
import logging

from urllib.request import Request, urlopen
from urllib.error import URLError, HTTPError

logger = logging.getLogger()
logger.setLevel(logging.INFO)

def lambda_handler(event, context):
    webhook_url = os.environ['WEBHOOK_URL']
    user_name = os.environ['USER_NAME']

    click_type = event['deviceEvent']['buttonClicked']['clickType']
    if click_type == "SINGLE":
        msg = "１回押されました"
    elif click_type == "DOUBLE":
        msg = "２回連続で押されました"
    elif click_type == "LONG":
        msg = "長押しされました"

    slack_message = {
        'username': user_name,
        'icon_emoji': ':face_with_thermometer:',
        'text': msg
    }

    req = Request(webhook_url, json.dumps(slack_message).encode('utf-8'))
    try:
        response = urlopen(req)
        response.read()
        logger.info("Message posted.")
    except HTTPError as e:
        logger.error("Request failed: %d %s", e.code, e.reason)
    except URLError as e:
        logger.error("Server connection failed: %s", e.reason)


dict1={1:0,2:1}

input_value=int(input("Enter a number :"))
prev=0
nextt=1
print(f"The fibonacci series upto {input_value} is {prev} {nextt} ",end="")
for i in range(1,input_value-1):
    a=prev+nextt
    print(a,end=" ")
    dict1[i+2]=a
    prev=nextt
    nextt=a

print()
print(f"Fibonacci series in dictionary is {dict1}")


import argparse
import os
import sys

import hypertune
import numpy as np
import torch

ECHO_DIR = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
sys.path.append(ECHO_DIR)
from models.agent import Agent
from utils.util_data import get_test_SNR_dbs, integers_to_symbols
from utils.util_data import add_cartesian_awgn as add_awgn
# from protocols.shared_preamble.trainer import trainer
from protocols.private_preamble.trainer import trainer

BER_TO_SNR = np.array([[1.00e-03, 1.04e+01],
                       [1.10e-02, 8.20e+00],
                       [2.10e-02, 7.40e+00],
                       [3.10e-02, 6.80e+00],
                       [4.10e-02, 6.20e+00],
                       [5.10e-02, 5.80e+00],
                       [6.10e-02, 5.40e+00],
                       [7.10e-02, 5.20e+00],
                       [8.10e-02, 4.80e+00],
                       [9.10e-02, 4.60e+00],
                       [1.01e-01, 4.20e+00],
                       [1.11e-01, 4.00e+00],
                       [1.21e-01, 3.80e+00],
                       [1.31e-01, 3.40e+00],
                       [1.41e-01, 3.20e+00],
                       [1.51e-01, 3.00e+00],
                       [1.61e-01, 2.80e+00],
                       [1.71e-01, 2.60e+00],
                       [1.81e-01, 2.20e+00],
                       [1.91e-01, 2.00e+00]])

def ber_to_snr(ber):
    if ber >= BER_TO_SNR[-1,0]:
        return 0
    bers = BER_TO_SNR[:, 0]
    index = np.argmin(np.abs(bers - ber))
    return BER_TO_SNR[index, 1]  # here is your result

BPS_TO_MOD_ORDER = {
    2: 'QPSK',
    3: '8PSK',
    4: 'QAM16'
}
TRAIN_SNRS_FOR_ORDER = {
    'QPSK': [13.0, 8.4, 4.2],
    '8PSK': [18.2, 13.2, 8.4],
    'QAM16': [20.0, 15.0, 10.4]
}
SNR_LEVEL_TO_INDEX = {
    'high': 0,
    'mid': 1,
    'low': 2
}

def get_ber(symbols1, symbols2):
    return np.sum(symbols1 ^ symbols2) / (symbols1.shape[0] * symbols1.shape[1])

def test(*,
         agent1,
         agent2,
         bits_per_symbol,
         test_SNR_db,
         signal_power=1.0,
         test_batch_size=100000,
         ):
    integers = np.random.randint(low=0, high=2 ** bits_per_symbol, size=[test_batch_size])
    preamble = integers_to_symbols(integers, bits_per_symbol=bits_per_symbol)
    A = agent1
    B = agent2
    c_signal_forward = A.mod.modulate(preamble, mode='exploit', dtype='cartesian')
    _c_signal_forward = B.mod.modulate(preamble, mode='exploit', dtype='cartesian')
    c_signal_forward_noisy = add_awgn(c_signal_forward, SNR_db=test_SNR_db, signal_power=signal_power)
    preamble_halftrip = B.demod.demodulate(c_signal_forward_noisy)  #
    c_signal_backward = B.mod.modulate(preamble_halftrip, mode='exploit', dtype='cartesian')
    c_signal_backward_noisy = add_awgn(c_signal_backward, SNR_db=test_SNR_db, signal_power=signal_power)
    preamble_roundtrip = A.demod.demodulate(c_signal_backward_noisy)

    _c_signal_forward_noisy = add_awgn(_c_signal_forward, SNR_db=test_SNR_db, signal_power=signal_power)
    _preamble_halftrip = A.demod.demodulate(_c_signal_forward_noisy)  #
    _c_signal_backward = A.mod.modulate(_preamble_halftrip, mode='exploit', dtype='cartesian')
    _c_signal_backward_noisy = add_awgn(_c_signal_backward, SNR_db=test_SNR_db, signal_power=signal_power)
    _preamble_roundtrip = B.demod.demodulate(_c_signal_backward_noisy)

    return (float(get_ber(preamble, preamble_roundtrip)) + float(get_ber(preamble, _preamble_roundtrip)))/2.0

def main():
    # Training settings
    parser = argparse.ArgumentParser(description='PyTorch Poly Echo')
    parser = add_experiment_args(parser)
    parser = add_mod_args(parser)
    parser = add_demod_args(parser)
    parser = add_poly_demod_args(parser)
    args = parser.parse_args()

    torch.manual_seed(args.seed)
    np.random.seed(args.seed)
    device = torch.device('cpu')

    agent_dict = {
        "bits_per_symbol": args.bits_per_symbol,
        "optimizer": "adam",
        "max_amplitude": 1.0,
        "demod_model": "poly",
        "mod_model": "poly",
        "mod_params": {
            "bits_per_symbol": args.bits_per_symbol,
            "stepsize_mu": args.stepsize_mu,
            "stepsize_sigma": args.stepsize_sigma,
            "initial_std": args.initial_std,
            "max_std": args.max_std,
            "min_std": args.min_std,
            "lambda_center": args.lambda_center,
            "lambda_l1": args.lambda_l1_mod,
            "lambda_l2": args.lambda_l2_mod,
            "lambda_baseline": args.lambda_baseline,
            "restrict_energy": 1,
            "max_amplitude": 1.0,
            "optimizer": "adam"
        },
        "demod_params": {
            "bits_per_symbol": args.bits_per_symbol,
            "optimizer": "adam",
            "stepsize_cross_entropy": args.stepsize_cross_entropy,
            "cross_entropy_weight": args.cross_entropy_weight,
            "epochs": args.epochs,
            "degree_polynomial": args.degree_polynomial,
            "lambda_l1": args.lambda_l1_demod,
            "lambda_l2": args.lambda_l2_demod,
        },
    }
    print(agent_dict)

    neural_dict = {
        "bits_per_symbol": args.bits_per_symbol,
        "optimizer": "adam",
        "max_amplitude": 1.0,
        "demod_model": "neural",
        "demod_params": {"activation_fn_hidden": "tanh", "cross_entropy_weight": 1, "hidden_layers": [50],
                         "loss_type": "l2", "stepsize_cross_entropy": 0.0010505808840839148,
                         "bits_per_symbol": 2, "max_amplitude": 1, "optimizer": "adam"},
        "max_amplitude": 1, "mod_model": "neural",
        "mod_params": {"activation_fn_hidden": "tanh", "hidden_layers": [50], "initial_std": 0.2,
                       "lambda_center": 0, "lambda_p": 0, "lambda_prob": 1e-10, "max_std": 100,
                       "min_std": 0.001, "restrict_energy": 1, "stepsize_mu": 0.0010887608407154701,
                       "stepsize_sigma": 0.00020228467654117958, "bits_per_symbol": 2, "max_amplitude": 1,
                       "optimizer": "adam"}, "optimizer": "adam"
    }

    agent1 = Agent(agent_dict=agent_dict, name='Poly')
    agent2 = Agent(agent_dict=agent_dict, name='Clone')

    A = agent1
    B = agent2
    last = False
    total_batches_sent = 0
    test_SNR_dbs = get_test_SNR_dbs()[args.bits_per_symbol]['ber_roundtrip']
    test_SNR = test_SNR_dbs[4]
    batches_interval = 0
    while total_batches_sent <= args.total_batches:
        if batches_interval == 0 or batches_interval - args.log_interval >= 0 or total_batches_sent == args.total_batches:
            batches_interval = 0
            roundtrip_ber = test(
                agent1=agent1,
                agent2=agent2,
                bits_per_symbol=args.bits_per_symbol,
                test_SNR_db=test_SNR,
                signal_power=1.0,
            )
            print('Test: Roundtrip BER: {:.4f}'.format(roundtrip_ber))
            hpt = hypertune.HyperTune()
            hpt.report_hyperparameter_tuning_metric(
                hyperparameter_metric_tag='roundtrip_ber',
                metric_value=roundtrip_ber,
                global_step=args.batch_size * total_batches_sent
            )
            hpt.report_hyperparameter_tuning_metric(
                hyperparameter_metric_tag='db_off',
                metric_value=test_SNR-ber_to_snr(roundtrip_ber),
                global_step=args.batch_size * total_batches_sent
            )
            # hpt.report_hyperparameter_tuning_metric(
            #     hyperparameter_metric_tag='agent1_centering_loss',
            #     metric_value=agent1.mod.model.location_loss(),
            #     global_step=args.batch_size * total_batches_sent
            # )
            # hpt.report_hyperparameter_tuning_metric(
            #     hyperparameter_metric_tag='agent2_centering_loss',
            #     metric_value=agent2.mod.model.location_loss(),
            #     global_step=args.batch_size * total_batches_sent
            # )
        B, A, batches_sent = trainer(agents=[A, B],
                                     bits_per_symbol=args.bits_per_symbol,
                                     batch_size=args.batch_size,
                                     train_SNR_db=TRAIN_SNRS_FOR_ORDER[BPS_TO_MOD_ORDER[args.bits_per_symbol]][
                                         SNR_LEVEL_TO_INDEX[args.train_snr]],
                                     signal_power=1.0,
                                     backwards_only=total_batches_sent == args.total_batches
                                     )
        total_batches_sent += batches_sent
        batches_interval += batches_sent

def add_mod_args(parser):
    """Adds arguments for modulator
    """
    parser.add_argument(
        '--stepsize-mu',
        type=float,
        default=0.001,
        metavar='LR',
        help='learning rate for mean of gaussian policy (default: 0.001)')
    parser.add_argument(
        '--stepsize-sigma',
        type=float,
        default=0.001,
        metavar='LR',
        help='learning rate for std of gaussian policy (default: 0.001)')
    parser.add_argument(
        '--initial-std',
        type=float,
        default=0.5,
        metavar='STD',
        help='std for gaussian policy (default: 0.5)')
    parser.add_argument(
        '--min-std',
        type=float,
        default=0.0001,
        metavar='STD',
        help='Min std for gaussian policy (default: 0.0001)')
    parser.add_argument(
        '--max-std',
        type=float,
        default=100.0,
        metavar='STD',
        help='Max std for gaussian policy (default: 100)')
    parser.add_argument(
        '--lambda-center',
        type=float,
        default=0.0,
        metavar='L',
        help='Weight penalizing un-centered constellations (default: 0.0)')
    parser.add_argument(
        '--lambda-baseline',
        type=float,
        default=0.0,
        metavar='L',
        help='Weight adding a baseline (default: 0.0)')
    parser.add_argument(
        '--lambda-l1-mod',
        type=float,
        default=0.0,
        metavar='L',
        help='L1 regularization weight (default: 0.0)')
    parser.add_argument(
        '--lambda-l2-mod',
        type=float,
        default=0.0,
        metavar='L',
        help='L2 regularization weight (default: 0.0)')
    return parser

def add_demod_args(parser):
    """Adds arguments for polynomial modulator
    """
    parser.add_argument(
        '--stepsize-cross-entropy',
        type=float,
        default=0.01,
        metavar='LR',
        help='learning rate for supervised demod learning (default: 0.001)')
    parser.add_argument(
        '--cross-entropy-weight',
        type=float,
        default=1.0,
        metavar='W',
        help='weight applied to update (default: 1.0)')
    parser.add_argument(
        '--epochs',
        type=int,
        default=1,
        metavar='N',
        help='Num epochs (default: 5)')
    parser.add_argument(
        '--lambda-l1-demod',
        type=float,
        default=0.0,
        metavar='L',
        help='L1 regularization weight (default: 0.0)')
    parser.add_argument(
        '--lambda-l2-demod',
        type=float,
        default=0.0,
        metavar='L',
        help='L2 regularization weight (default: 0.0)')
    return parser

def add_poly_demod_args(parser):
    parser.add_argument(
        '--degree-polynomial',
        type=int,
        default=3,
        metavar='N',
        help='degree of polynomial (default: 3)')
    return parser

def add_experiment_args(parser):
    parser.add_argument(
        '--seed',
        type=int,
        default=1,
        metavar='S',
        help='random seed (default: 1)')
    parser.add_argument(
        '--train-snr',
        type=str,
        default='mid',
        metavar='LEVEL',
        choices=['high', 'mid', 'low'],
        help='signal to noise ratio for training (default: "mid")')
    parser.add_argument(
        '--log-interval',
        type=int,
        default=2000,
        metavar='N',
        help='how many batches to wait before logging training status (default: 250)')
    parser.add_argument(
        '--total-batches',
        type=int,
        default=10000,
        metavar='N',
        help='how many batches to train for (default: 10000)')
    parser.add_argument(
        '--batch-size',
        type=int,
        default=32,
        metavar='N',
        help='How many symbols in a preamble (default: 32)')
    parser.add_argument(
        '--bits-per-symbol',
        type=int,
        default=2,
        metavar='N',
        choices=[2, 3, 4],
        help='Modulation scheme (default: 2 for QPSK)')
    parser.add_argument(
        '--model-dir',
        default=None,
        help='The directory to store the model')
    parser.add_argument('--job-dir',  # handled automatically by AI Platform
                        help='GCS location to write checkpoints and export ' \
                             'models')
    return parser

if __name__ == '__main__':
    main()


import hail as hl
from hail import Table
from hail.typecheck import typecheck, nullable, oneof, sequenceof
from hail.utils import wrap_to_list, new_temp_file

@typecheck(ht=Table,
           key=str,
           value=str,
           fields=str)
def gather(ht, key, value, *fields) -> Table:
    """Collapse fields into key-value pairs.

    :func:`.gather` mimics the functionality of the `gather()` function found in R's
    ``tidyr`` package. This is a way to turn "wide" format data into "long"
    format data.

    Parameters
    ----------
    ht : :class:`.Table`
        A Hail table.
    key : :class:`str`
        The name of the key field in the gathered table.
    value : :class:`str`
        The name of the value field in the gathered table.
    fields : variable-length args of obj:`str`
        Names of fields to gather in ``ht``.

    Returns
    -------
    :class:`.Table`
        Table with original ``fields`` gathered into ``key`` and ``value`` fields."""

    ht = ht.annotate(_col_val=hl.array([
        hl.struct(field_name=field, value=ht[field]) for field in fields]))
    ht = ht.drop(*fields)
    ht = ht.explode(ht['_col_val'])
    ht = ht.annotate(**{key: ht['_col_val'][0],
                        value: ht['_col_val'][1]})
    ht = ht.drop('_col_val')

    ht_tmp = new_temp_file()
    ht.write(ht_tmp)

    return hl.read_table(ht_tmp)

@typecheck(ht=Table,
           field=str,
           value=str,
           key=nullable(oneof(str,
                              sequenceof(str))))
def spread(ht, field, value, key=None) -> Table:
    """Spread a key-value pair of fields across multiple fields.

    :func:`.spread` mimics the functionality of the `spread()` function in R's
    `tidyr` package. This is a way to turn "long" format data into "wide"
    format data.

    Given a ``field``, :func:`.spread` will create a new table by grouping
    ``ht`` by its row key and, optionally, any additional fields passed to the
    ``key`` argument.

    After collapsing ``ht`` by these keys, :func:`.spread` creates a new row field
    for each unique value of ``field``, where the row field values are given by the
    corresponding ``value`` in the original ``ht``.

    Parameters
    ----------
    ht : :class:`.Table`
        A Hail table.
    field : :class:`str`
        The name of the factor field in `ht`.
    value : :class:`str`
        The name of the value field in `ht`.
    key : optional, obj:`str` or list of :class:`str`
        The name of any fields to group by, in addition to the
        row key fields of ``ht``.

    Returns
    -------
    :class:`.Table`
        Table with original ``key`` and ``value`` fields spread across multiple columns."""

    if key is None:
        key = list(ht.key)
    else:
        key = wrap_to_list(key)
        key = list(ht.key) + key

    field_vals = list(ht.aggregate(hl.agg.collect_as_set(ht[field])))
    ht = (ht.group_by(*key)
            .aggregate(**{rv: hl.agg.take(ht[rv], 1)[0] for rv in ht.row_value if rv not in set(key + [field, value])},
                       **{fv: hl.agg.filter(ht[field] == fv,
                                            hl.rbind(
                                                hl.agg.take(ht[value], 1),
                                                lambda take: hl.if_else(hl.len(take) > 0,
                                                                        take[0],
                                                                        'NA'))) for fv in field_vals}))

    ht_tmp = new_temp_file()
    ht.write(ht_tmp)

    return ht

@typecheck(ht=Table,
           field=str,
           into=sequenceof(str),
           delim=oneof(str, int))
def separate(ht, field, into, delim) -> Table:
    """Separate a field into multiple fields by splitting on a delimiter
    character or position.

    :func:`.separate` mimics the functionality of the `separate()` function in R's
    ``tidyr`` package.

    This function will create a new table where ``field`` has been split into
    multiple new fields, whose names are given by ``into``.

    If ``delim`` is a ``str`` (including regular expression strings), ``field``
    will be separated into columns by that string. In this case, the length
    of ``into`` must match the number of resulting fields.

    If ``delim`` is an ``int``, ``field`` will be separated into two row fields,
    where the first field contains the first ``delim`` characters of ``field``
    and the second field contains the remaining characters.

    Parameters
    ----------
    ht : :class:`.Table`
        A Hail table.
    field : :class:`str`
        The name of the field to separate in ``ht``.
    into : list of :class:`str`
        The names of the fields to create by separating ``field``.
    delimiter : :class:`str` or :obj:`int`
        The character or position by which to separate ``field``.

    Returns
    -------
    :class:`.Table`
        Table with original ``field`` split into fields whose names are defined
        by `into`."""

    if isinstance(delim, int):
        ht = ht.annotate(**{into[0]: ht[field][:delim],
                            into[1]: ht[field][delim:]})
    else:
        split = ht[field].split(delim)
        ht = ht.annotate(**{into[i]: split[i] for i in range(len(into))})
    ht = ht.drop(field)

    ht_tmp = new_temp_file()
    ht.write(ht_tmp)

    return ht


from datetime import datetime
import os

from flask import abort, flash, Flask, redirect, render_template, url_for
from flask_mail import Message
from sqlalchemy.orm import joinedload

from qwc_config_db.config_models import ConfigModels
from qwc_services_core.database import DatabaseEngine
from forms import RegistrationForm

class RegistrationGUI:
    """RegistrationGUI class

    Provide application form for group membership and create registration
    requests.
    """

    def __init__(self, mail, i18n, logger):
        """Constructor

        :param flask_mail.Mail mail: Application mailer
        :param callable i18n: Translation helper method
        :param Logger logger: Application logger
        """
        self.mail = mail
        self.i18n = i18n
        self.logger = logger

        # load ORM models for ConfigDB
        self.db_engine = DatabaseEngine()
        self.config_models = ConfigModels(self.db_engine)

        self.RegistrableGroup = self.config_models.model('registrable_groups')
        self.RegistrationRequest = self.config_models.model(
            'registration_requests'
        )
        self.User = self.config_models.model('users')

        # get recipients for admin notifications
        self.admin_recipients = os.environ.get('ADMIN_RECIPIENTS')
        if self.admin_recipients:
            self.admin_recipients = self.admin_recipients.split(',')

    def register(self, identity):
        """Show application form and submit membership requests.

        :param obj identity: User identity
        """
        # check if user is signed in
        username = self.username(identity)
        if username is None:
            self.logger.info("User is not signed in")
            abort(403)

        session = self.session()

        # find user
        user = session.query(self.User).filter_by(name=username).first()
        if user is None:
            self.logger.warning("Could not find user '%s'" % username)
            abort(404)

        # query registrable groups
        query = session.query(self.RegistrableGroup) \
            .order_by(self.RegistrableGroup.title)
        # eager load relations
        query = query.options(joinedload(self.RegistrableGroup.group))
        registrable_groups = query.all()
        registrable_groups_titles = {}

        # query pending registration requests for this user
        query = session.query(self.RegistrationRequest) \
            .filter_by(user_id=user.id, pending=True)
        pending_requests = query.all()
        pending_registrable_ids = [
            r.registrable_group_id for r in pending_requests
        ]

        # query group memberships
        user_groups = user.sorted_groups
        user_group_ids = [g.id for g in user_groups]

        session.close()

        # create form
        form = RegistrationForm()

        form.registrable_groups = []
        form.groups.choices = []
        form.unsubscribe_groups.choices = []
        for registrable_group in registrable_groups:
            pending = registrable_group.id in pending_registrable_ids
            member = registrable_group.group_id in user_group_ids

            # add registrable group
            form.registrable_groups.append({
                'id': registrable_group.id,
                'title': registrable_group.title,
                'description': registrable_group.description,
                'pending': pending,
                'member': member
            })

            # add lookup for registrable group titles
            registrable_groups_titles[registrable_group.id] = \
                registrable_group.title

            if not pending:
                if member:
                    # add unsubscribable group choice
                    form.unsubscribe_groups.choices.append(
                        (registrable_group.id, registrable_group.title)
                    )
                else:
                    # add registrable group choice
                    form.groups.choices.append(
                        (registrable_group.id, registrable_group.title)
                    )

        if form.is_submitted():
            if form.validate():
                if form.groups.data or form.unsubscribe_groups.data:
                    # use same creation time for all requests in the form
                    created_at = datetime.utcnow()

                    session = self.session()

                    # create registration requests
                    for registrable_group_id in form.groups.data:
                        registration_request = self.RegistrationRequest(
                            user_id=user.id,
                            registrable_group_id=registrable_group_id,
                            unsubscribe=False,
                            pending=True,
                            created_at=created_at
                        )
                        session.add(registration_request)

                    # create unsubscribe requests
                    for registrable_group_id in form.unsubscribe_groups.data:
                        registration_request = self.RegistrationRequest(
                            user_id=user.id,
                            registrable_group_id=registrable_group_id,
                            unsubscribe=True,
                            pending=True,
                            created_at=created_at
                        )
                        session.add(registration_request)

                    session.commit()
                    session.close()

                    self.send_admin_notification(
                        user, form, registrable_groups_titles
                    )

                    flash(self.i18n('registration.flash.submitted'), 'success')
                    return redirect(url_for('register'))
                else:
                    flash(
                        self.i18n('registration.flash.no_group_selected'),
                        'warning'
                    )
            else:
                self.logger.warning(form.errors)
                flash(self.i18n('registration.flash.failed'), 'error')

        return render_template(
            'registration.html', title=self.i18n('registration.title'),
            form=form, username=user.name
        )

    def send_admin_notification(self, user, form, registrable_groups_titles):
        """Send mail with registration requests to admin users.

        :param User user: User instance
        :param FlaskForm form: Registration form
        :param obj registrable_groups_titles: Lookup for registrable group
                                              titles by id
        """
        # collect requested registrable group titles
        groups = [
            registrable_groups_titles.get(id) for id in form.groups.data
        ]
        unsubscribe_groups = [
            registrable_groups_titles.get(id)
            for id in form.unsubscribe_groups.data
        ]

        if (
            (not groups and not unsubscribe_groups)
            or not self.admin_recipients
        ):
            # no requests or no admin recipients
            return

        # send notification to admin users
        try:
            msg = Message(
                self.i18n('admin_notification.subject'),
                recipients=self.admin_recipients
            )
            # set message body from template
            msg.body = render_template(
                'admin_notification.txt', user=user, groups=groups,
                unsubscribe_groups=unsubscribe_groups
            )

            # send message
            self.logger.debug(msg)
            self.mail.send(msg)
        except Exception as e:
            self.logger.error(
                "Could not send notification to admins %s:\n%s" %
                (self.admin_recipients, e)
            )

    def username(self, identity):
        """Extract username from identity.

        :param obj identity: User identity
        """
        username = None
        if isinstance(identity, dict):
            username = identity.get('username')
        else:
            # identity is username
            username = identity

        return username

    def session(self):
        """Return new session for ConfigDB."""
        return self.config_models.session()


#
# ps8pr1.py (Problem Set 8, Problem 1)
#
# 2D Lists
#
# Computer Science 111
#
# If you worked with a partner, put his or her contact info below:
# partner's name:
# partner's email:
#

# IMPORTANT: This file is for your solutions to Problem 1.
# Your solutions to problem 2 should go in ps8pr2.py instead.

from __future__ import print_function

import random

def create_grid(height, width):
    """ creates and returns a 2-D list of 0s with the specified dimensions.
        inputs: height and width are non-negative integers
    """
    grid = []

    for r in range(height):
        row = [0] * width     # a row containing width 0s
        grid += [row]

    return grid

def print_grid(grid):
    """ prints the 2-D list specified by grid in 2-D form,
        with each row on its own line, and nothing between values.
        input: grid is a 2-D list. We assume that all of the cell
               values are integers between 0 and 9.
    """
    height = len(grid)
    width = len(grid[0])

    for r in range(height):
        for c in range(width):
            print(grid[r][c], end='')   # print nothing between values
        print()                         # at end of row, go to next line

def diagonal_grid(height, width):
    """ creates and returns a height x width grid in which the cells
        on the diagonal are set to 1, and all other cells are 0.
        inputs: height and width are non-negative integers
    """
    grid = create_grid(height, width)   # initially all 0s

    for r in range(height):
        for c in range(width):
            if r == c:
                grid[r][c] = 1

    return grid

def inner_grid(height, width):
    """ creates and returns a 2-D list of height rows and width columns in
    which the “inner” cells are all 1 and the cells on the outer border are all 0
    """
    grid = create_grid(height, width)   # initially all 0s

    for r in range(1,height-1):
        for c in range(1,width-1):
            grid[r][c] = 1

    return grid

def random_grid(height, width):
    """ creates and returns a 2-D list of height rows and width columns in which the inner
    cells are randomly assigned either 0 or 1, but the cells on the outer border are all 0
    """
    grid = create_grid(height, width)   # initially all 0s

    for r in range(1,height-1):
        for c in range(1,width-1):
            grid[r][c] = random.choice([0, 1])

    return grid

def copy(grid):
    """creates and returns a deep copy of grid–a new,
    separate 2-D list that has the same dimensions and cell values as grid
    """
    height = len(grid)
    width = len(grid[0])
    new_grid = create_grid(height, width)   # initially all 0s

    for r in range(height):
        for c in range(width):
            new_grid[r][c] = grid[r][c]

    return new_grid

def invert(grid):
    """takes an existing 2-D list of 0s and 1s and inverts it – changing
    all 0 values to 1, and changing all 1 values to 0
    """
    height = len(grid)
    width = len(grid[0])

    for r in range(height):
        for c in range(width):
            if grid[r][c] == 0:
                grid[r][c] = 1
            elif grid[r][c] == 1:
                grid[r][c] = 0


# 사용자가 입력한 숫자의 약수를 텍스트 파일에 기록
# def divisor(num):
#     divisors = []
#     for i in range(1, num + 1):
#         if num % i == 0:
#             divisors.append(i)
#
#     return divisors
#
#
# path = "C:/Users/YUN/Python/"
#
# n = int(input('0보다 큰 정수 입력 : '))
# if n > 0:
#     with open(path + 'divisor.txt', 'a') as f:
#         f.write(f'{n}의 약수 : {divisor(n)}\n')
#         print('divisor write complete')
# else:
#     print('0보가 큰 수를 입력하세요')
#
# with open(path + 'divisor.txt', 'r') as f:
#     line = f.readline()
#     while line != '':
#         print(f'{line}')
#         line = f.readline()

# 사용자가 입력한 숫자의 소수를 텍스트 파일에 기록

def isPrime(num):
    primes = []
    for i in range(2, num):
        isFlag = True
        for j in range(2, i):
            if i % j == 0:
                isFlag = False
                break

        if isFlag:
            primes.append(i)
    return primes

path = "C:/Users/YUN/Python/"

n = int(input('0보다 큰 정수 입력 : '))
if n > 0:
    with open(path + 'primes.txt', 'a') as f:
        f.write(f'{n}까지의 소수 : {isPrime(n)}\n')
        print('prime write complete')
else:
    print('0보가 큰 수를 입력하세요')

with open(path + 'primes.txt', 'r') as f:
    line = f.readline()
    while line != '':
        print(f'{line}')
        line = f.readline()


import pygame
import ast
from sprites import *

class Game:
    """Roda o jogo e manipula as demais classes.
    """
    def __init__(self, size:tuple[int,int]) -> None:
        """
        :Inicializa a classe Game:

        Args:
            size (tuple[int,int]): dimensões da janela do jogo.
        """
        pygame.init()
        self.window = pygame.display.set_mode(size)
        self.background = pygame.image.load('images\\background.png')
        self.background = pygame.transform.smoothscale(self.background.convert_alpha(), (800, 700))
        self.level = 0
        self.screen = "INITIAL"
        self.run = True
        self.page = "INITIAL"
        self.background_music = pygame.mixer.Sound("sounds\\som_de_fundo.wav")
        self.background_music.play(-1)

    def event_treatment(self) -> None:
        """Trata os eventos do input.
        """
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.run = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_SPACE and self.page == "INITIAL":
                    self.page = "SELECT"
                if event.key == pygame.K_SPACE and self.page == "VICTORY":
                    self.page = "PLAYING"
                    if self.level < 14:
                        self.level += 1
                    else:
                        self.page = "SELECT"

                if event.key == pygame.K_q:
                    self.run = False

    def loop(self) -> None:
        """Responsável por efetivamente rodar o jogo dentro de seu loop interno.
        """
        self.screen = Screen(self.window)
        self.button = Button()
        while self.run:
            self.window.blit(self.background, (0, 0))
            self.event_treatment()
            if self.page == "INITIAL":
                self.screen.front_page()
            if self.page == "SELECT":
                self.screen.select_level()
                self.level = self.button.event_treatment()
                self.run = self.button.run
                if isinstance(self.level, int):
                    L = Level(self.level)
                    self.page = L.run_level(self.window, self.background, self.screen)

            if self.page == "PLAYING":
                L = Level(self.level)
                self.page = L.run_level(self.window, self.background, self.screen)

            if self.page == "VICTORY":
                self.screen.victory()
            pygame.display.flip()

class Level():
    """Cria, manipula e renderiza as fases (levels) do jogo.
    """
    def __init__(self, level:int) -> None:
        """Inicializa a classe Level.

        Args:
            level (int): número da fase selecionada
        """
        self.level = level
        self.moving_sprites = pygame.sprite.Group()
        with open('levels.txt', 'r') as arq:
            file = arq.readlines()

        self.all_levels_info = [ast.literal_eval(line) for line in file]
        self.create_positions()
        self.top_left_x = (800 - self.dimension[0][0]*30) // 2 - 20
        self.top_left_y = (700 - self.dimension[0][1]*30) // 2
        self.create_level()
        self.level_running = True

    def create_positions(self) -> None:
        """Define as posições dos objetos (sprites) da fase selecionada.
        """
        self.player_position = self.all_levels_info[self.level]['robot_position']
        self.boxes_positions = self.all_levels_info[self.level]['boxes_positions']
        self.goal_positions = self.all_levels_info[self.level]['goal_positions']
        self.walls_positions = self.all_levels_info[self.level]['walls_positions']
        self.walk_positions = self.all_levels_info[self.level]['walk_positions']
        self.dimension = self.all_levels_info[self.level]['dimension']

    def create_level(self) -> None:
        """Cria os sprite da fase selecionada
        """
        #Cria os objetos em suas respectivas categorias
        for tipo in [[self.walk_positions, Floor], [self.walls_positions, Wall], [self.goal_positions, Goal], [self.boxes_positions, Box]]:
            #Retira todos os objetos possivelmente existentes na categoria
            tipo[1].objects.clear()
            #Adiciona os objetos
            for block in tipo[0]:
                object = tipo[1](convert_coordinates(block, self.top_left_x, self.top_left_y))
                self.moving_sprites.add(object)
        self.player = Robot(convert_coordinates(self.player_position[0], self.top_left_x, self.top_left_y))
        self.moving_sprites.add(self.player)

    def run_level(self, window:pygame.Surface, background:pygame.Surface, screen) -> str:
        """

        Args:
            window (pygame.Surface): janela de execução do jogo
            background (pygame.Surface): imagem de fundo
            screen (Screen): objeto do tipo screen, cria o layout da tela

        Returns:
            str: informação de qual screen está sendo executada (tela inicial, jogo, tela de vitória etc)
        """
        clock = pygame.time.Clock()
        start_time = pygame.time.get_ticks()
        movimentos = 0
        while self.level_running:
            window.blit(background, (0, 0))
            screen.game(movimentos, start_time, self.top_left_y, self.level)
            movimentos += self.player.robot_event_treatment()
            self.moving_sprites.draw(window)

            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.level_running = False
                    pygame.display.quit()
                    quit()
                if event.type == pygame.KEYDOWN:
                    if event.key == pygame.K_q:
                        self.level_running = False
                        pygame.display.quit()
                        quit()
                    if event.key == pygame.K_ESCAPE:
                        self.level_running = False
                        return "SELECT"
                    if event.key == pygame.K_r:
                        self.create_positions()
                        self.create_level()
                        self.run_level(window, background, screen)
                        self.level_running = False
                        return "SELECT"

                if all([box.in_goal for box in Box.objects]):
                    self.level_running = False
                    pygame.mixer.Sound("sounds\\som_de_vitoria.wav").play()
                    return "VICTORY"

            pygame.display.flip()
            clock.tick(7)

class Screen():
    """Classe das telas exibidas durante o jogo (tela da fase, tela inicial, tela de vitória)
    """
    def __init__(self, window:pygame.Surface) -> None:
        """Inicializa objeto da classe Screen.

        Args:
            window (pygame.Surface): _description_
        """
        self.window = window
        self.width = window.get_width()
        self.height = window.get_height()
        self.font = 'fonts/Symtext.ttf'

    def front_page(self) -> None:
        """Renderiza a tela inicial do jogo.
        """
        font = pygame.font.Font(self.font, 100)
        text = font.render('ROBOX', True, (40, 40, 40))
        self.window.blit(text, (self.width / 2 - (text.get_width() / 2), self.height / 2 - 200))
        text = font.render('FOREVER', True, (15,15,15))
        self.window.blit(text, (self.width / 2 - (text.get_width() / 2), self.height / 2 - 100))

        font = pygame.font.Font(self.font, 20)
        text = font.render('Press SPACE to continue', True, (10,10,10))
        self.window.blit(text, (self.width / 2 - (text.get_width() / 2), self.height / 2 + 50))

    def select_level(self) -> None:
        """Renderiza a tela de seleção de fase.
        """
        font = pygame.font.Font(self.font, 60)
        text = font.render('SELECT LEVEL', True, (30, 30, 30))
        self.window.blit(text, (self.width / 2 - (text.get_width() / 2), 80))

        level = 0
        for y in range(3):
            for x in range(5):
                dim = 80
                pos_x = x*(self.width/5 - 20)-dim/2 + 120
                pos_y = y*((self.height - 300)/3) + dim + 150
                title = 'LEVEL ' + str(level + 1)
                font = pygame.font.Font(self.font, 15)
                text = font.render(title, True, (30, 30, 30))
                self.window.blit(text, ((pos_x + (dim - text.get_width())/2, pos_y - text.get_height())))
                rect_level = pygame.Rect(pos_x, pos_y, dim, dim)
                image_level = pygame.image.load('images\\level' + str(level + 1) + '.png')
                image_level = pygame.transform.smoothscale(image_level.convert_alpha(), (dim, dim))
                self.window.blit(image_level, (pos_x, pos_y))

                Button.objects.append(rect_level)
                level += 1

    def game(self, movimentos:int, start_time:int, top_left_y:int, level:int) -> None:
        """Renderiza os textos da tela da fase sendo jogada.

        Args:
            movimentos (int): quantidade de vezes de o jogador moveu uma caixa
            start_time (int): tempo inicial da fase
            top_left_y (int): ordenada do canto superior do mapa da fase
            level (int): número da fase selecionada
        """
        title = title = 'LEVEL ' + str(level + 1)
        font = pygame.font.Font(self.font, 20)
        text = font.render(f'MOVIMENTOS: {movimentos}', True, (30, 30, 30))
        textRect = text.get_rect()
        textRect.center = (100, top_left_y)
        self.window.blit(text, textRect)
        counting_time = pygame.time.get_ticks() - start_time
        counting_seconds = str( round((counting_time)/1000) ).zfill(1)
        counting_text = font.render(f'TIMER: {counting_seconds}', True, (30, 30, 30))
        counting_rect = counting_text.get_rect()
        counting_rect.center = (650, top_left_y)
        self.window.blit(counting_text, counting_rect)
        font = pygame.font.Font(self.font, 60)
        text = font.render(title, True, (30, 30, 30))
        self.window.blit(text, (self.width / 2 - (text.get_width() / 2), 80))

    def victory(self) -> None:
        """Renderiza a tela de vitória.
        """
        font = pygame.font.Font(self.font, 60)
        label = font.render('CONGRATULATIONS!!!', True, (150, 150, 0))
        self.window.blit(label, (self.width / 2 - (label.get_width() / 2), self.height / 2 - 50))

        font = pygame.font.Font(self.font, 20)
        label = font.render('Press SPACE to continue', True, (150, 150, 0))
        self.window.blit(label, (self.width / 2 - (label.get_width() / 2), self.height / 2 + 50))

class Button:
    """Classe dos botões

    Returns:
        int: número da fase selecionada
    """
    objects = []
    def __init__(self) -> None:
        """Inicializa objeto da classe Button
        """
        self.run = True
    def event_treatment(self) -> int:
        """Trata os eventos do input referentes ao Button.

        Returns:
            int: retorna o número da fase escolhida
        """
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                self.run = False
            if event.type == pygame.KEYDOWN:
                if event.key == pygame.K_q:
                    self.run = False
            if event.type == pygame.MOUSEBUTTONUP:
                mouse_position = pygame.mouse.get_pos()
                for button in self.__class__.objects:
                    if button.collidepoint(mouse_position):
                        return self.__class__.objects.index(button)

def convert_coordinates(position:tuple[int,int], x:int, y:int) -> tuple[int,int]:
    """Converte as coordenadas do formato recebido no arquivo levels.txt para o utilizado no jogo

    Args:
        position (tuple[int,int]): coordenadas de posição no formato do arquivo levels.txt
        x (int): constante para shiftar a posição no eixo horizontal
        y (int): constante para shiftar a posição no eixo vertical

    Returns:
        tuple[int,int]: coordenadas de posição no formato do jogo
    """
    return ((position[0]*30 + x, position[1]*30 + y))

if __name__ == '__main__':
    G = Game((800, 700))
    G.loop()