# spencer stanley

import requests
from collections import defaultdict

"""
functions to query data from smash.gg,
including set results and player identifiers,
and then construct a graph of losses from
this info
"""

def get_melee_rankings():
    """get all regional, state, national, and multinational
    rankings for super smash bros melee

    output
    ---------
    rankings: dict of {ranking id: ranking name}
    """
    rankings = requests.get(
        'https://api.smash.gg/rankings?per_page=100&\
        filter={%22videogameIds%22:%221%22,%22global%22:true,\
        %22regional%22:%22state%22}'
    ).json()['items']['entities']['rankingSeries']

    rankings += requests.get(
        'https://api.smash.gg/rankings?expand[]=players&per_page=100&\
        filter={%22videogameIds%22:%221%22,%22regional%22:%22country%22}'
    ).json()['items']['entities']['rankingSeries']

    rankings += requests.get(
        'https://api.smash.gg/rankings?expand[]=players&per_page=100&\
        filter={%22videogameIds%22:%221%22,%22regional%22:%22subState%22}'
    ).json()['items']['entities']['rankingSeries']

    rankings = {
        r['id']:r['name'] for r in rankings
    }

    return rankings

def get_sgg_phases(tournaments):
    """return list of json objects containing
    phase data for each given smash.gg tournament URL
    by querying the sgg API for each phase

    input
    ---------
    tournaments: list of json blobs (requests.get().json)
    for smashgg tournament & event:
    e.g., for tournament t and event e at that tournament,
    http://api.smash.gg/tournament/t/event/e?expand[]=groups
    """
    # get IDs of phase groups to query
    ids = []
    for tournament in tournaments:
        for phase_group in tournament['entities']['groups']:
            ids.append(phase_group['id'])

    # query smashgg API for each phase group
    phases = []
    base_url = 'http://api.smash.gg/phase_group/%s\
                ?expand[]=sets&expand[]=seeds'
    for pg_id in ids:
        phases.append(
            requests.get(base_url % pg_id).json()  # append json objects
        )

    return phases

def get_sgg_players(phases, rankings):
    """return {player id: {various player information}}
    for given smash.gg tournament's phases and
    a dictionary of {ranking id: ranking name}

    input
    ---------
    phases: as output by get_sgg_phases()
    rankings: as output by get_melee_rankings()

    output
    ---------
    players: {player id:{
        tag: player's gamertag
        name: player's real name,
        country: player's home country,
        state: if country is US or Canada, then state/province,
        region: subregion if applicable (e.g. NorCal),
        rankings: {ranking name: number} (e.g. SSBMRank: 1),
        image: height, width, and url of profile image, if applicable
        }
    }
    """
    players = {}

    for p in phases:
        for s in p['entities']['seeds']:
            pid = s['mutations']['entrants'].keys()[0]
            p_info = s['mutations']['players'].values()[0]
            players.update(
                {pid:
                 {
                     'tag': p_info['gamerTag'],
                     'name': p_info['name'],
                     'country': p_info['country'],
                     'state': p_info['state'],
                     'region': p_info['region'],
                     'rankings': {
                         rankings[r['seriesId']]:r['rank']
                         for r in p_info['rankings']
                         if r['seriesId'] in rankings
                     },
                     'image': {
                         'height':p_info['images'][0]['height'],
                         'width':p_info['images'][0]['width'],
                         'url':p_info['images'][0]['url']
                     } if len(p_info['images']) > 0
                      else None
                 }
                }
            )

    return players

def convert_players(pid_to_dict):
    """convert output of get_sgg_players to
    dictionary keyed by player tags

    input
    ---------
    pid_to_dict: dict of playerID:{player info},
    as output by get_sgg_players()

    output
    ---------
    players: {tag: all other information}
    """
    players = {
        players2[p]['tag'] : {
            subitem:(players2[p][subitem] if subitem != 'ssbmrank'
                     else players2[p][subitem][0]
                     if len(players2[p][subitem]) > 0
                     else None)
            for subitem in players2[p] if subitem != 'tag'
        }
        for p in players2
    }
    return players

def add_to_graph(phases, players, graph=None):
    """add player losses for a tournament's phases
    to graph for analysis

    input
    ---------
    phases: list of tournament phase group json objects,
    as per get_sgg_phases()
    players: dictionary of {ID:tag}, as per get_sgg_players()
    graph (optional): include if appending tournaments to
    preexisting data; should be of the form
    defaultdict(lambda: defaultdict(int))

    output
    ---------
    graph: dictionary of {losing player : {winning player : n wins}}
    """
    if graph is None:
        graph = defaultdict(lambda: defaultdict(int))

    for p in phases:
        for s in p['entities']['sets']:
            winner = players.get(str(s['winnerId']))
            loser = players.get(str(s['loserId']))
            if winner is not None and loser is not None and \
                s['entrant1Score'] >= 0 and s['entrant2Score'] >= 0:
                graph[loser['tag']][winner['tag']] += 1

    return graph


import datetime as dt
from collections import defaultdict
from io import BytesIO

import openpyxl
from django.test import TestCase

from reporting.reports import ProductionReport
from robots.models import Robot

class ReportingReportsTests(TestCase):

    @classmethod
    def setUpClass(cls):
        super().setUpClass()
        cls.valid_data = [
            {
                'model': 'R2',
                'version': 'D2',
                'created': '2023-10-02 00:00:00',
            },
            {
                'model': 'R2',
                'version': 'D2',
                'created': '2023-10-08 23:59:59',
            },
            {
                'model': 'R2',
                'version': 'G7',
                'created': '2023-10-08 23:59:59',
            },
            {
                'model': 'S5',
                'version': 'K8',
                'created': '2023-10-08 12:40:59',
            },
        ]
        cls.invalid_data = [
            {
                'model': 'S5',
                'version': 'K8',
                'created': '2023-10-01 23:59:59',
            },
            {
                'model': 'S5',
                'version': 'K8',
                'created': '2023-10-09 00:00:00',
            },
            {
                'model': 'S5',
                'version': 'K8',
                'created': '2022-10-08 00:00:00',
            },
            {
                'model': 'I5',
                'version': 'K8',
                'created': '2022-10-08 00:00:00',
            },
        ]
        cls.test_data = cls.invalid_data + cls.valid_data

        for item in cls.test_data:
            Robot.objects.create(
                model=str(item['model']),
                version=str(item['version']),
                created=(dt.datetime.fromisoformat(item['created'])
                         .replace(tzinfo=dt.timezone.utc)),
                serial=f"{item['model']}-{item['version']}",
            )

    def test_production_report_collects_correct_statistics(self):
        """Экземляр класса ProductionReport формирует
        корректный словарь со статистикой."""
        year = 2023
        week = 40
        expected_stat = defaultdict(lambda: defaultdict(int))
        for item in self.valid_data:
            expected_stat[item['model']][item['version']] += 1

        report = ProductionReport(week, year)
        self.assertEqual(report.total_robots, len(self.valid_data))
        for model in report.statistics.keys():
            for version in report.statistics[model].keys():
                with self.subTest(robot=(model, version)):
                    self.assertEqual(
                        report.statistics[model][version],
                        expected_stat[model][version]
                    )

    def test_write_report_method_saves_correct_file(self):
        """Экземляр класса ProductionReport сохраняет
        корректный словарь со статистикой."""
        year = 2023
        week = 40
        file = BytesIO()
        report = ProductionReport(week, year)
        report.write_report(file)

        workbook = openpyxl.load_workbook(file, read_only=True)
        expected_sheetnames = (
            ['total'] + sorted(report.statistics.keys())
        )
        self.assertEqual(workbook.sheetnames, expected_sheetnames)
        total_cell = workbook.get_sheet_by_name('total').cell(2, 2)
        self.assertEqual(total_cell.value, report.total_robots)

        for model in report.statistics.keys():
            exp_model_total = sum(report.statistics[model].values())
            exp_version_count = len(report.statistics[model].keys())
            model_sheet = workbook.get_sheet_by_name(model)
            model_total_cell = model_sheet.cell(model_sheet.max_row, 3)
            with self.subTest(model=model):
                self.assertEqual(model_total_cell.value, exp_model_total)
                self.assertEqual(model_sheet.max_row - 2, exp_version_count)
        file.close()


"""
Complete the mutate_string function in the editor below.

mutate_string has the following parameters:

string string: the string to change
int position: the index to insert the character at
string character: the character to insert
"""

def mutate_string(string, position, character):
    s_list = list(string)
    s_list[position] = character
    return ''.join(s_list)

if __name__ == '__main__':
    s = input()
    i, c = input().split()
    s_new = mutate_string(s, int(i), c)
    print(s_new)


import os
import time

DIRS = [
    {"path": "./X", "mode": 0o777},
    {"path": "./X/Y1", "mode": 0o777},
    {"path": "./X/Y1/Y11", "mode": 0o777},
    {"path": "./X/Y1/Y12", "mode": 0o777},
    {"path": "./X/Y2", "mode": 0o777},
    {"path": "./X/Y2/Y11", "mode": 0o777},
    {"path": "./X/Y2/Y12", "mode": 0o777},
]

FILES = [
    {"path": "./X/a.txt", "mode": 0o777, "content": "abcdefg..."},
    {"path": "./X/b.txt", "mode": 0o777, "content": "abcdefg..."},
    {"path": "./X/Y1/pi.txt", "mode": 0o444, "content": "3.14159..."},
    {"path": "./X/Y1/Y11/tmp.txt", "mode": 0o777, "content": ""},
    {"path": "./X/Y1/Y12/empty.jpg", "mode": 0o777, "content": ""},
    {"path": "./X/Y2/e.txt", "mode": 0o644, "content": "1.619..."},
    {"path": "./X/Y2/Y11/e.txt", "mode": 0o531, "content": "1.619....ABC"},
    {"path": "./X/Y2/e.txt.tmp", "mode": 0o644, "content": "abcdef..ghij..."},
    {"path": "./X/Y2/Y12/f?f#$f.txt", "mode": 0o614, "content": "1.619A.."},
    {"path": "./X/Y2/Y12/g g.txt", "mode": 0o612, "content": "1.619AB."},
    {"path": './X/Y2/Y12/h"h".txt', "mode": 0o613, "content": "1.619ABC"},
    {"path": './X/Y2/Y12/h"i".back', "mode": 0o613, "content": "1.619ABCD"},
    {"path": './X/Y2/Y12/xyz.txt.back', "mode": 0o613, "content": "1.619ABCDE"}
]

FILES_CREATION_DELAY = 1

class TestFilesCreator:
    def __init__(
        self,
        dirs: list = DIRS,
        files: list = FILES,
        delay: float = FILES_CREATION_DELAY,
    ) -> None:
        self.dirs = dirs
        self.files = files
        self.delay = delay

    def prepare_env(self) -> None:
        print("Preparing test files")
        self._create_dirs()
        self._create_files()

    def _create_dirs(self) -> None:
        print("Creating directories...")
        for dir in self.dirs:
            try:
                os.mkdir(dir["path"], dir["mode"])
                print(f"Created {dir['path']}")
                # delay is needed to allow for the deletion of newer files
                time.sleep(self.delay)
            except Exception as e:
                print(e)

    def _create_files(self) -> None:
        print("Creating files...")
        for file in self.files:
            try:
                with open(file["path"], "w") as f:
                    f.write(file["content"])
                os.chmod(file["path"], file["mode"])
                print(f"Created {file['path']}")
                # delay is needed to allow for the deletion of newer files
                time.sleep(self.delay)
            except Exception as e:
                print(e)


import tensorflow as tf
from tensorflow import keras

#import lib.model as model
import lib.model_Michele as model
import lib.ops as ops

class TRGAN(keras.Model):
    def __init__(self, FLAGS):
        super(TRGAN, self).__init__()
        self.FLAGS = FLAGS

        #self.netE = model.encoder(FLAGS.nei, FLAGS.nc, FLAGS.kW, FLAGS.sW, FLAGS.nef, FLAGS.nBottleneck)
        #self.netG = model.decoder(FLAGS.nBottleneck, FLAGS.noiseGen, FLAGS.nz, FLAGS.ngf, FLAGS.nc, FLAGS.ngo)
        #self.netD = model.discriminator(FLAGS.conditionAdv, FLAGS.nei, FLAGS.nc, FLAGS.kW, FLAGS.sW, FLAGS.ndf, FLAGS.ngo)
        self.netE = model.encoder_Michele(FLAGS.nei, FLAGS.nc, FLAGS.kW, FLAGS.sW)
        self.netG = model.decoder_Michele(FLAGS.nc, FLAGS.ngo)
        self.netD = model.discriminator_Michele(FLAGS.ngo, FLAGS.nc)

        self.mse_loss_tracker = keras.metrics.Mean(name="mse_loss")
        self.vel_grad_loss_tracker = keras.metrics.Mean(name="vel_grad_loss")
        self.content_loss_tracker = keras.metrics.Mean(name="content_loss")
        self.adversarial_loss_tracker = keras.metrics.Mean(name="adversarial_loss")
        self.gen_loss_tracker = keras.metrics.Mean(name="gen_loss")

        self.epoch_step = tf.Variable(0, trainable=False)

        self.global_train_batch = tf.Variable(0, trainable=False, dtype=tf.int64)
        self.train_summary_writer = tf.summary.create_file_writer(FLAGS.summary_dir + '/train')

        self.global_validation_batch = tf.Variable(0, trainable=False, dtype=tf.int64)
        self.validation_summary_writer = tf.summary.create_file_writer(FLAGS.summary_dir + '/validation')

    def call(self, inputs):
        # Build the generator part
        latent_vector = self.netE(inputs)

        if self.FLAGS.noiseGen:
            noise_shape = tf.concat([tf.shape(inputs)[0:1], tf.constant([1, 1, self.FLAGS.nz])], 0)

            if self.FLAGS.noisetype == 'uniform':
                noise = tf.random.uniform(shape=noise_shape, minval=-1, maxval=1, seed=1)
            elif self.FLAGS.noisetype == 'normal':
                noise = tf.random.normal(shape=noise_shape, seed=1)

            return self.netG([latent_vector, noise])
        else:
            return self.netG(latent_vector)

    def initialize(self):
        if self.FLAGS.checkpoint is not None:
            if self.FLAGS.pre_trained_generator:
                print("Restoring generator weights from {}".format(self.FLAGS.checkpoint))
                ckpt = tf.train.Checkpoint(netE=self.netE, netG=self.netG)
                status = ckpt.read(self.FLAGS.checkpoint)
                status.assert_existing_objects_matched()
            elif self.FLAGS.pre_trained_model:
                print("Restoring model weights from {}".format(self.FLAGS.checkpoint))
                ckpt = tf.train.Checkpoint(netE=self.netE, netG=self.netG, netD=self.netD)
                status = ckpt.read(self.FLAGS.checkpoint)
                status.assert_existing_objects_matched()
            else:
                print("Restoring weights from {}".format(self.FLAGS.checkpoint))
                self.load_weights(self.FLAGS.checkpoint)

# =============================================================================
#     def velocity_grad_loss(self, context_train, gap_train, gap_train_pred):
#         dg_dx = ops.tf_ddx_fft(context_train, gap_train, self.FLAGS.mask)
#         dg_dx_pred = ops.tf_ddx_fft(context_train, gap_train_pred, self.FLAGS.mask)
#         dg_dx_rms = tf.math.reduce_mean( tf.math.square(dg_dx), axis=[1, 2, 3], keepdims=True )
#         ddx_loss = tf.math.reduce_mean( tf.math.square(dg_dx_pred - dg_dx) / dg_dx_rms )
#
#         dg_dy = ops.tf_ddy_fft(context_train, gap_train, self.FLAGS.mask)
#         dg_dy_pred = ops.tf_ddy_fft(context_train, gap_train, self.FLAGS.mask)
#         dg_dy_rms = tf.math.reduce_mean( tf.math.square(dg_dy), axis=[1, 2, 3], keepdims=True )
#         ddy_loss = tf.math.reduce_mean( tf.math.square(dg_dy_pred - dg_dy) / dg_dy_rms )
#
#         vel_grad_loss = ddx_loss + ddy_loss
#
#         return vel_grad_loss
# =============================================================================

    def compile(self, d_optimizer, g_optimizer):
        super(TRGAN, self).compile()
        self.d_optimizer = d_optimizer
        self.g_optimizer = g_optimizer

    def train_step(self, data):
        # Unpack the data.
        context_train, gap_train = data

        gap_train_pred = self(context_train, training=True)  # Predict the fake images

        # Train the discriminator
        with tf.GradientTape() as tape:
            if self.FLAGS.conditionAdv:
                discrim_fake_output = self.netD([context_train, gap_train_pred])
                discrim_real_output = self.netD([context_train, gap_train])
            else:
                discrim_fake_output = self.netD(gap_train_pred)
                discrim_real_output = self.netD(gap_train)

            discrim_fake_loss = tf.math.log(1 - discrim_fake_output + self.FLAGS.EPS)
            discrim_real_loss = tf.math.log(discrim_real_output + self.FLAGS.EPS)

            discrim_loss = tf.math.reduce_mean(-(discrim_fake_loss + discrim_real_loss))

        # Log the discriminator output
        self.global_train_batch.assign_add(1)
        with self.train_summary_writer.as_default():
            tf.summary.scalar('discriminator output', tf.math.reduce_mean(discrim_fake_output), step=self.global_train_batch)

        # Compute gradients
        trainable_vars = self.netD.trainable_variables
        gradients = tape.gradient(discrim_loss, trainable_vars)

        # Update weights
        self.d_optimizer.apply_gradients(zip(gradients, trainable_vars))

        # Train the generator (note that we should *not* update the weights
        # of the discriminator)!
        with tf.GradientTape() as tape:
            gap_train_pred = self(context_train, training=True)  # Forward pass
            # Compute our own losses
            # mse loss
            #wtl2Matrix = ops.tf_wtl2Matrix(gap_train, self.FLAGS.overlapPred)
            #gap_train_rms = tf.math.reduce_mean( wtl2Matrix * tf.math.square(gap_train), axis=[1, 2, 3], keepdims=True )
            #mse_loss = tf.math.reduce_mean( wtl2Matrix * tf.math.square(gap_train_pred - gap_train) / gap_train_rms )
            mse_loss = tf.math.reduce_mean( tf.math.square(gap_train_pred - gap_train) )
            # velocity gradient loss
            vel_grad_loss = 0#self.velocity_grad_loss(context_train, gap_train, gap_train_pred)
            # Content loss => mse + velocity gradient
            content_loss = (1 - self.FLAGS.lambda_vel_grad) * mse_loss + self.FLAGS.lambda_vel_grad * vel_grad_loss

            # Adversarial loss
            if self.FLAGS.conditionAdv:
                discrim_fake_output = self.netD([context_train, gap_train_pred])
            else:
                discrim_fake_output = self.netD(gap_train_pred)

            adversarial_loss = tf.math.reduce_mean(-tf.math.log(discrim_fake_output + self.FLAGS.EPS))

            # Generator loss => content loss + adversarial loss
            gen_loss = (1 - self.FLAGS.adversarial_ratio) * content_loss + (self.FLAGS.adversarial_ratio) * adversarial_loss

        # Compute gradients
        trainable_vars = self.netE.trainable_variables + self.netG.trainable_variables
        gradients = tape.gradient(gen_loss, trainable_vars)

        # Update weights
        self.g_optimizer.apply_gradients(zip(gradients, trainable_vars))

        # Compute our own metrics
        self.mse_loss_tracker.update_state(mse_loss)
        self.vel_grad_loss_tracker.update_state(vel_grad_loss)
        self.content_loss_tracker.update_state(content_loss)
        self.adversarial_loss_tracker.update_state(adversarial_loss)
        self.gen_loss_tracker.update_state(gen_loss)
        return {m.name: m.result() for m in self.metrics}

    @property
    def metrics(self):
        # We list our `Metric` objects here so that `reset_states()` can be
        # called automatically at the start of each epoch
        # or at the start of `evaluate()`.
        # If you don't implement this property, you have to call
        # `reset_states()` yourself at the time of your choosing.
        return [self.mse_loss_tracker, self.vel_grad_loss_tracker, self.content_loss_tracker, self.adversarial_loss_tracker, self.gen_loss_tracker]

    def test_step(self, data):
        # Unpack the data.
        context_train, gap_train = data
        # Compute predictions
        gap_train_pred = self(context_train, training=False)
        # Updates the metrics tracking the loss
        # mse loss
        #wtl2Matrix = ops.tf_wtl2Matrix(gap_train, self.FLAGS.overlapPred)
        #gap_train_rms = tf.math.reduce_mean( wtl2Matrix * tf.math.square(gap_train), axis=[1, 2, 3], keepdims=True )
        #mse_loss = tf.math.reduce_mean( wtl2Matrix * tf.math.square(gap_train_pred - gap_train) / gap_train_rms )
        mse_loss = tf.math.reduce_mean( tf.math.square(gap_train_pred - gap_train) )
        self.mse_loss_tracker.update_state(mse_loss)
        # velocity gradient loss
        vel_grad_loss = 0#self.velocity_grad_loss(context_train, gap_train, gap_train_pred)
        self.vel_grad_loss_tracker.update_state(vel_grad_loss)
        # Content loss => mse + velocity gradient
        content_loss = (1 - self.FLAGS.lambda_vel_grad) * mse_loss + self.FLAGS.lambda_vel_grad * vel_grad_loss
        self.content_loss_tracker.update_state(content_loss)

        # Adversarial loss
        if self.FLAGS.conditionAdv:
            discrim_fake_output = self.netD([context_train, gap_train_pred])
        else:
            discrim_fake_output = self.netD(gap_train_pred)

        # Log the discriminator output
        self.global_validation_batch.assign_add(1)
        with self.validation_summary_writer.as_default():
            tf.summary.scalar('discriminator output', tf.math.reduce_mean(discrim_fake_output), step=self.global_validation_batch)

        adversarial_loss = tf.math.reduce_mean(-tf.math.log(discrim_fake_output + self.FLAGS.EPS))
        self.adversarial_loss_tracker.update_state(adversarial_loss)

        # Generator loss => content loss + adversarial loss
        gen_loss = (1 - self.FLAGS.adversarial_ratio) * content_loss + (self.FLAGS.adversarial_ratio) * adversarial_loss
        self.gen_loss_tracker.update_state(gen_loss)

        # Return a dict mapping metric names to current value.
        # Note that it will include the loss (tracked in self.metrics).
        return {m.name: m.result() for m in self.metrics}


'''
Created on 01-04-2013

@author: firemark
'''
from grazyna.modules import get_args_from_text, check_type
from grazyna.utils.types import range_int

import pytest

def func_test(
        bot, arg1, arg2,
        karg: range_int(0, 10)=0,
        akarg: int=2):
    pass

func_test.varkw = None
func_test.varargs = None
func_test.cmd = 'cmd'

@pytest.mark.parametrize('text,cor_args,cor_kwargs', [
    ("firemark tiramo", ["firemark", "tiramo"], {}),
    ("tiramo firemark with spaces", ["tiramo", "firemark with spaces"], {}),
    (
        "tiramo firemark with spaces karg= 2 akarg= 5",
        ["tiramo", "firemark with spaces"],
        {"karg": "2", "akarg": "5"}
    ),
    (
        '"tiramo with spaces" firemark karg=" spaces "',
        ["tiramo with spaces", "firemark"],
        {"karg": " spaces "},
    ),
    (
        'karg= firemark arg2= 20 10 ',
        ["10"],
        {"karg": "firemark", "arg2": "20"}
    ),
])
def test__get_args(text, cor_args, cor_kwargs):
    max_args = 2
    args, kwargs = get_args_from_text(text, max_args)
    assert args == cor_args
    assert kwargs == cor_kwargs

@pytest.mark.parametrize('args,kwargs,result', [
    # without kwargs
    (["t1", "t2"], {}, {"arg1": "t1", "arg2": "t2", "karg": 0, "akarg": 2}),
    # with optionals
    (
        ["t", "t", "-1", "-1"], {},
        {"arg1": "t", "arg2": "t", "karg": 0, "akarg": -1}
    ),
    (  # mixing
        ["ta", "t"], {"karg": "5", "akarg": "-1"},
        {"arg1": "ta", "arg2": "t", "karg": 5, "akarg": -1}
    ),
    (  # another
        ["t", "t"], {"karg": "15", "akarg": "10"},
        {"arg1": "t", "arg2": "t", "karg": 10, "akarg": 10}
    ),
    (  # without args
        [], {"arg1": "a", "arg2": "c", "karg": "5", "akarg": "10"},
        {"arg1": "a", "arg2": "c", "karg": 5, "akarg": 10}
    ),
])
def test__check_type(args, kwargs, result):
    assert check_type(args, kwargs, func_test) == result


# This script used to create and train wide and deep model on Kaggle's Criteo Dataset

import time
import argparse
import tensorflow as tf
import math
import collections
import numpy as np
import os
import sys

# Set to INFO for tracking training, default is WARN. ERROR for least messages
tf.compat.v1.logging.set_verbosity(tf.compat.v1.logging.INFO)
print("Using TensorFlow version %s" % (tf.__version__))
CONTINUOUS_COLUMNS = ["I" + str(i) for i in range(1, 14)]  # 1-13 inclusive
CATEGORICAL_COLUMNS = ["C" + str(i) for i in range(1, 27)]  # 1-26 inclusive
LABEL_COLUMN = ["clicked"]
TRAIN_DATA_COLUMNS = LABEL_COLUMN + CONTINUOUS_COLUMNS + CATEGORICAL_COLUMNS
FEATURE_COLUMNS = CONTINUOUS_COLUMNS + CATEGORICAL_COLUMNS
IDENTIY_COLUMNS = ["I10"]
HASH_BUCKET_SIZES = {
    'C1': 2500,
    'C2': 2000,
    'C3': 300000,
    'C4': 250000,
    'C5': 1000,
    'C6': 100,
    'C7': 20000,
    'C8': 4000,
    'C9': 20,
    'C10': 100000,
    'C11': 10000,
    'C12': 250000,
    'C13': 40000,
    'C14': 100,
    'C15': 100,
    'C16': 200000,
    'C17': 50,
    'C18': 10000,
    'C19': 4000,
    'C20': 20,
    'C21': 250000,
    'C22': 100,
    'C23': 100,
    'C24': 250000,
    'C25': 400,
    'C26': 100000
}

HASH_BUCKET_SIZES_LARGE = {
    'C1': 2500,
    'C2': 2000,
    'C3': 5000000,
    'C4': 1500000,
    'C5': 1000,
    'C6': 100,
    'C7': 20000,
    'C8': 4000,
    'C9': 20,
    'C10': 100000,
    'C11': 10000,
    'C12': 5000000,
    'C13': 40000,
    'C14': 100,
    'C15': 100,
    'C16': 3000000,
    'C17': 50,
    'C18': 10000,
    'C19': 4000,
    'C20': 20,
    'C21': 4000000,
    'C22': 100,
    'C23': 100,
    'C24': 250000,
    'C25': 400,
    'C26': 100000
}

IDENTITY_NUM_BUCKETS = {'I10': 10}

EMBEDDING_DIMENSIONS = {
    'C1': 64,
    'C2': 64,
    'C3': 128,
    'C4': 128,
    'C5': 64,
    'C6': 64,
    'C7': 64,
    'C8': 64,
    'C9': 64,
    'C10': 128,
    'C11': 64,
    'C12': 128,
    'C13': 64,
    'C14': 64,
    'C15': 64,
    'C16': 128,
    'C17': 64,
    'C18': 64,
    'C19': 64,
    'C20': 64,
    'C21': 128,
    'C22': 64,
    'C23': 64,
    'C24': 128,
    'C25': 64,
    'C26': 128
}

def generate_input_fn(filename, batch_size, num_epochs):
    def parse_csv(value):
        tf.compat.v1.logging.info('Parsing {}'.format(filename))
        cont_defaults = [[0.0] for i in range(1, 10)]
        cont_defaults = cont_defaults + [[0]]
        cont_defaults = cont_defaults + [[0.0] for i in range(11, 14)]
        cate_defaults = [[" "] for i in range(1, 27)]
        label_defaults = [[0]]
        column_headers = TRAIN_DATA_COLUMNS
        record_defaults = label_defaults + cont_defaults + cate_defaults
        columns = tf.io.decode_csv(value, record_defaults=record_defaults)
        all_columns = collections.OrderedDict(zip(column_headers, columns))
        labels = all_columns.pop(LABEL_COLUMN[0])
        features = all_columns
        return features, labels

    # Extract lines from input files using the Dataset API.
    dataset = tf.data.TextLineDataset(filename)
    dataset = dataset.shuffle(buffer_size=20000,
                              seed=2021)  # fix seed for reproducing
    dataset = dataset.repeat(num_epochs)
    dataset = dataset.prefetch(batch_size)
    dataset = dataset.batch(batch_size)
    dataset = dataset.map(parse_csv, num_parallel_calls=28)
    dataset = dataset.prefetch(1)
    return dataset

def build_feature_cols(train_file_path, test_file_path):
    # Statistics of Kaggle's Criteo Dataset has been calculated in advance to save time
    print('****Computing statistics of train dataset*****')
    # with open(train_file_path, 'r') as f, open(test_file_path, 'r') as f1:
    #     nums = [line.strip('\n').split(',') for line in f.readlines()
    #             ] + [line.strip('\n').split(',') for line in f1.readlines()]
    #     numpy_arr = np.array(nums)
    #     mins_list, max_list, range_list = [], [], []
    #     for i in range(len(TRAIN_DATA_COLUMNS)):
    #         if TRAIN_DATA_COLUMNS[i] in CONTINUOUS_COLUMNS:
    #             col_min = numpy_arr[:, i].astype(np.float32).min()
    #             col_max = numpy_arr[:, i].astype(np.float32).max()
    #             mins_list.append(col_min)
    #             max_list.append(col_max)
    #             range_list.append(col_max - col_min)
    mins_list = [
        0.0, -3.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0
    ]
    range_list = [
        1539.0, 22069.0, 65535.0, 561.0, 2655388.0, 233523.0, 26297.0, 5106.0,
        24376.0, 9.0, 181.0, 1807.0, 6879.0
    ]

    def make_minmaxscaler(min, range):
        def minmaxscaler(col):
            return (col - min) / range

        return minmaxscaler

    deep_columns = []
    wide_columns = []
    embedding_columns = []
    for column_name in FEATURE_COLUMNS:
        if column_name in IDENTITY_NUM_BUCKETS:
            categorical_column = tf.feature_column.categorical_column_with_identity(
                column_name, num_buckets=IDENTITY_NUM_BUCKETS[column_name])
            wide_columns.append(categorical_column)
            deep_columns.append(
                tf.feature_column.indicator_column(categorical_column))
        elif column_name in CATEGORICAL_COLUMNS:
            categorical_column = tf.feature_column.categorical_column_with_hash_bucket(
                column_name,
                hash_bucket_size=HASH_BUCKET_SIZES[column_name],
                dtype=tf.string)
            wide_columns.append(categorical_column)

            embedding_columns.append(
                tf.feature_column.embedding_column(
                    categorical_column,
                    dimension=EMBEDDING_DIMENSIONS[column_name],
                    combiner='mean'))
        else:
            normalizer_fn = None
            i = CONTINUOUS_COLUMNS.index(column_name)
            col_min = mins_list[i]
            col_range = range_list[i]
            normalizer_fn = make_minmaxscaler(col_min, col_range)
            column = tf.feature_column.numeric_column(
                column_name, normalizer_fn=normalizer_fn, shape=(1, ))
            wide_columns.append(column)
            deep_columns.append(column)

    return wide_columns, deep_columns, embedding_columns

def wide_optimizer():
    opt = tf.compat.v1.train.FtrlOptimizer(
        learning_rate=args.linear_learning_rate,
        l1_regularization_strength=args.linear_l1_regularization,
        l2_regularization_strength=args.linear_l2_regularization)
    return opt

def deep_optimizer():
    learning_rate_fn = args.deep_learning_rate
    opt = tf.compat.v1.train.AdagradOptimizer(learning_rate=learning_rate_fn,
                                              initial_accumulator_value=0.1,
                                              use_locking=False)
    return opt

def build_estimator(model_dir=None,
                    train_file_path=None,
                    test_file_path=None,
                    wide_optimizer=None,
                    deep_optimizer=None,
                    deep_dropout=0.0,
                    deep_hidden_units=[1024, 512, 256],
                    protocol='grpc'):
    if model_dir is None:
        model_dir = 'models/model_WIDE_AND_DEEP_' + str(int(time.time()))
        print("Model directory = %s" % model_dir)

    wide_columns, deep_columns, embedding_columns = build_feature_cols(
        train_file_path, test_file_path)
    deep_columns += embedding_columns
    print ("======================== real protocol: ", protocol)
    runconfig = tf.estimator.RunConfig(
        protocol=protocol,
        save_checkpoints_steps=args.save_steps,
        keep_checkpoint_max=args.keep_checkpoint_max,
        tf_random_seed=2021)  # fix random seed for reproducing
    if args.bf16:
        m = tf.estimator.DNNLinearCombinedClassifier(
            config=runconfig,
            model_dir=model_dir,
            linear_feature_columns=wide_columns,
            linear_optimizer=wide_optimizer,
            linear_sparse_combiner='sum',
            dnn_feature_columns=deep_columns,
            dnn_optimizer=deep_optimizer,
            dnn_dropout=deep_dropout,
            dnn_dtype=tf.bfloat16,
            dnn_hidden_units=deep_hidden_units,
            loss_reduction=tf.losses.Reduction.SUM_OVER_BATCH_SIZE)
    else:
        m = tf.estimator.DNNLinearCombinedClassifier(
            config=runconfig,
            model_dir=model_dir,
            linear_feature_columns=wide_columns,
            linear_optimizer=wide_optimizer,
            linear_sparse_combiner='sum',
            dnn_feature_columns=deep_columns,
            dnn_optimizer=deep_optimizer,
            dnn_dropout=deep_dropout,
            dnn_hidden_units=deep_hidden_units,
            loss_reduction=tf.losses.Reduction.SUM_OVER_BATCH_SIZE)
    print('estimator built')
    return m

def get_arg_parser():
    parser = argparse.ArgumentParser()
    parser.add_argument('--protocol',
                        help='Protocol, grpc/grpc++/star_server',
                        required=False,
                        default='grpc')
    parser.add_argument('--data_location',
                        help='Full path of train data',
                        required=False,
                        default='./data')
    parser.add_argument('--steps',
                        help='set the number of steps on train dataset',
                        type=int,
                        default=0)
    parser.add_argument('--batch_size',
                        help='Batch size to train. Default is 512',
                        type=int,
                        default=512)
    parser.add_argument('--output_dir',
                        help='Full path to logs & model output directory',
                        required=False,
                        default='./result')
    parser.add_argument('--checkpoint',
                        help='Full path to checkpoints input/output directory',
                        required=False)
    parser.add_argument('--deep_dropout',
                        help='Dropout regularization for deep model',
                        type=float,
                        default=0.0)
    parser.add_argument('--linear_l1_regularization',
                        help='L1 regularization for linear model',
                        type=float,
                        default=0.0)
    parser.add_argument('--linear_l2_regularization',
                        help='L2 regularization for linear model',
                        type=float,
                        default=0.0)
    parser.add_argument('--linear_learning_rate',
                        help='Learning rate for linear model',
                        type=float,
                        default=0.2)
    parser.add_argument('--deep_learning_rate',
                        help='Learning rate for deep model',
                        type=float,
                        default=0.05)
    parser.add_argument('--profile_steps',
                        help='Save steps of profile hooks, zero to close',
                        type=int,
                        default=0)
    parser.add_argument('--large_version',
                        help='Use large version model',
                        default=False)
    parser.add_argument('--save_steps',
                        help='set the number of steps on saving checkpoints',
                        type=int,
                        default=500)
    parser.add_argument('--bf16',
                        help='enable DeepRec BF16 in deep model. Default FP32',
                        default=False)
    parser.add_argument('--keep_checkpoint_max',
                        help='Maximum number of recent checkpoint to keep',
                        type=int,
                        default=1)

    return parser

if __name__ == "__main__":
    parser = get_arg_parser()
    args = parser.parse_args()

    print("Begin training and evaluation")
    # check dataset
    train_file = args.data_location + '/train.csv'
    test_file = args.data_location + '/eval.csv'
    if (not os.path.exists(train_file)) or (not os.path.exists(test_file)):
        print(
            '------------------------------------------------------------------------------------------'
        )
        print(
            "train.csv or eval.csv does not exist in the given data_location. Please provide valid path"
        )
        print(
            '------------------------------------------------------------------------------------------'
        )
        sys.exit()
    no_of_training_examples = sum(1 for line in open(train_file))
    no_of_test_examples = sum(1 for line in open(test_file))
    print("Numbers of training dataset is {}".format(no_of_training_examples))
    print("Numbers of test dataset is {}".format(no_of_test_examples))

    # set batch size & steps
    batch_size = args.batch_size
    if args.steps == 0:
        no_of_epochs = 10
        train_steps = math.ceil(
            (float(no_of_epochs) * no_of_training_examples) / batch_size)
    else:
        no_of_epochs = math.ceil(
            (float(batch_size) * args.steps) / no_of_training_examples)
        train_steps = args.steps
    test_steps = math.ceil(float(no_of_test_examples) / batch_size)

    # set directory path
    model_dir = os.path.join(args.output_dir,
                             'model_WIDE_AND_DEEP_' + str(int(time.time())))
    checkpoint_dir = args.checkpoint if args.checkpoint else model_dir
    print("Saving model checkpoints to " + checkpoint_dir)
    export_dir = args.output_dir

    protocol = args.protocol
    print("protocol is: ", protocol)
    # create model by estimator
    if args.large_version:
        print("Large Version")
        HASH_BUCKET_SIZES = HASH_BUCKET_SIZES_LARGE
        m = build_estimator(checkpoint_dir, train_file, test_file,
                            wide_optimizer, deep_optimizer, args.deep_dropout,
                            [1536, 768, 64], protocol=protocol)
    else:
        print("Small Version")
        m = build_estimator(checkpoint_dir, train_file, test_file,
                            wide_optimizer, deep_optimizer, args.deep_dropout,
                            [1024, 512, 256], protocol=protocol)

    # add hooks
    hooks = []
    if args.profile_steps > 0:
        hooks.append(
            tf.estimator.ProfilerHook(save_steps=args.profile_steps,
                                      output_dir=checkpoint_dir))

    # train & evaluate model
    train_spec = tf.estimator.TrainSpec(input_fn=lambda: generate_input_fn(
        train_file, batch_size, int(no_of_epochs)),
                                        max_steps=int(train_steps),
                                        hooks=hooks)
    eval_spec = tf.estimator.EvalSpec(
        input_fn=lambda: generate_input_fn(test_file, batch_size, 1),
        steps=test_steps)
    results = tf.estimator.train_and_evaluate(m, train_spec, eval_spec)

    TF_CONFIG = os.getenv('TF_CONFIG', 'null')
    if TF_CONFIG == 'null':
        print('Accuracy: %s' % results[0]['accuracy'])
        print('AUC: %s' % results[0]['auc'])
    else:
        print(results)


import peewee
from db_connection import db

class Table10(peewee.Model):
    Part_id = peewee.AutoField()
    Part = peewee.TextField()
    Cat = peewee.CharField()

    class Meta:
        database = db
        db_table = "Table10"

class Table20(peewee.Model):
    Catnumb = peewee.AutoField()
    Cat_name = peewee.CharField(50)
    Price = peewee.CharField(50)

    class Meta:
        database = db
        db_table = "Table20"

Table20.drop_table()
Table10.drop_table()
Table10.create_table()
Table20.create_table()

Apartment = Table10(Part_id=1, Part="Квартиры", Cat=505)
Apartment.save()
Car = Table10(Part_id=2, Part="Автомашины", Cat=205)
Car.save()
Boards = Table10(Part_id=3, Part="Доски", Cat=10)
Boards.save()
Cabinets = Table10(Part_id=4, Part="Шкафы", Cat=30)
Cabinets.save()
Books = Table10(Part_id=5, Part="Книги", Cat=160)
Books.save()

Building_materials = Table20(Catnumb=10, Cat_name="Стройматериалы", Price=105.00)
Building_materials.save()
Real_estate = Table20(Catnumb=505, Cat_name="Недвижимость", Price=210.00)
Real_estate.save()
Transport = Table20(Catnumb=205, Cat_name="Транспорт", Price=160.00)
Transport.save()
Furniture = Table20(Catnumb=30, Cat_name="Мебель", Price=77.00)
Furniture.save()
Technique = Table20(Catnumb=45, Cat_name="Техника", Price=65.00)
Technique.save()


import gym
import matplotlib.pyplot as plt
import numpy as np
import tensorflow as tf
from tensorflow.keras.layers import Dense, Input
from tensorflow.keras.models import Model
from tensorflow.keras.optimizers import Adagrad, SGD

N = 1000

class Policy:
    def __init__(self, in_size, out_size, hidden_sizes, lr=1e-1):
        in_l = Input(in_size)
        hl = in_l
        for size in hidden_sizes:
            hl = Dense(size, activation='tanh')(hl)
        out_l = Dense(out_size, activation='softmax', use_bias=False)(hl)
        self.out_size = out_size
        self.model = Model(in_l, out_l)
        self.model.build(in_size)
        self.optimizer = tf.keras.optimizers.Adagrad(1e-1)
        self.__train_on_batch_tf = tf.function(self.__train_on_batch,
            input_signature=[tf.TensorSpec(shape=(None, in_size), dtype=tf.float32),
                tf.TensorSpec(shape=None, dtype=tf.int32),
                tf.TensorSpec(shape=None, dtype=tf.float32)])

    def __train_on_batch(self, states, actions, advantages):
        def loss():
            pi = self.model(states)
            a_one_hot = tf.one_hot(actions, self.out_size)
            log_pi = tf.math.log(tf.reduce_sum(pi * a_one_hot, 1))
            return -tf.reduce_sum(log_pi * advantages)
        self.optimizer.minimize(loss, self.model.trainable_variables)

    def partial_fit(self, states, actions, advantages):
        states_2d = np.atleast_2d(states)
        advantages_1d = np.atleast_1d(advantages)
        actions_1d = np.atleast_1d(actions)
        return self.__train_on_batch_tf(states_2d, actions_1d, advantages_1d)

    @tf.function
    def __predict_tf(self, state):
        return self.model(state)

    def predict(self, state):
        state_2d = np.atleast_2d(state)
        return self.__predict_tf(state_2d).numpy()[0]

    def sample_action(self, state):
        p = self.predict(state)
        return np.random.choice(len(p), p=p)

class Value:
    def __init__(self, in_size, hidden_sizes, lr=1e-4):
        in_l = Input(in_size)
        hl = in_l
        for size in hidden_sizes:
            hl = Dense(size, activation='tanh')(hl)
        out_l = Dense(1)(hl)
        self.model = Model(in_l, out_l)
        self.model.build(in_size)
        self.optimizer = tf.keras.optimizers.SGD(1e-4)
        self.__train_on_batch_tf = tf.function(self.__train_on_batch,
            input_signature=[tf.TensorSpec(shape=(None, in_size), dtype=tf.float32),
                tf.TensorSpec(shape=None, dtype=tf.float32)])

    def __train_on_batch(self, states, returns):
        def loss():
            y_hat = tf.reshape(self.model(states), [-1])
            return tf.reduce_sum(tf.math.square(returns - y_hat))
        self.optimizer.minimize(loss, self.model.trainable_variables)

    def partial_fit(self, states, returns):
        states_2d = np.atleast_2d(states)
        returns_1d = np.atleast_1d(returns)
        return self.__train_on_batch_tf(states_2d, returns_1d)

    @tf.function
    def __predict_tf(self, state):
        return self.model(state)

    def predict(self, state):
        state_2d = np.atleast_2d(state)
        return self.__predict_tf(state_2d).numpy()[0]

def play_one_mc(env, policy, value, gamma=0.99):
    # Init
    total_rew=0
    done=False
    states = []
    actions = []
    rewards = []
    returns = []
    advantages = []
    obs = env.reset()
    r=0

    # While not game over
    while not done:
        # Sample action and store current (s,a,r) in memory
        a = policy.sample_action(obs)
        states.append(obs)
        actions.append(a)
        rewards.append(r)
        # Step
        obs_prime, r, done, _ = env.step(a)
        total_rew += r
        # If the pole fell
        if done:
            r = -200
        # Update obs
        obs = obs_prime

    # Save the final (s,a,r)
    action = policy.sample_action(obs)
    states.append(obs)
    actions.append(a)
    rewards.append(r)

    # Compute returns and advantages
    G = 0
    for s, r in zip(reversed(states), reversed(rewards)):
        returns.append(G)
        advantages.append(G - value.predict(s))
        G = r + gamma * G
    returns.reverse()
    advantages.reverse()

    # Update the models
    policy.partial_fit(states, actions, advantages)
    value.partial_fit(states, returns)

    # Return tot rew
    return total_rew

def play_one_ac(env, policy, value, gamma=0.99):
    # Init
    total_rew=0
    done=False
    obs = env.reset()

    # While not game over
    while not done:
        # Sample action
        a = policy.sample_action(obs)
        # Step
        obs_prime, r, done, _ = env.step(a)
        total_rew += r
        # Update the models
        V_prime = value.predict(obs_prime)
        G = r + gamma * V_prime
        advantage = G - value.predict(obs)
        policy.partial_fit(obs, a, advantage)
        value.partial_fit(obs, G)
        # Update obs
        obs = obs_prime

    # Return rew
    return total_rew

# Init
env = gym.make('CartPole-v0')
in_size = env.observation_space.shape[0]
out_size = env.action_space.n
policy = Policy(in_size, out_size, [])
value = Value(in_size, [10])
total_rewards = np.empty(N)
losses = np.empty(N)

# Main loop
for n in range(N):
    total_rewards[n] = play_one_mc(env, policy, value)
    if((n+1) % 10 == 0):
        print("episode:", n+1, "tot reward:", total_rewards[n], "avg reward (last 100):", total_rewards[max(0, n-100):(n+1)].mean())

# Plot rew
plt.plot(total_rewards)
running_avg = np.empty(len(total_rewards))
for t in range(len(total_rewards)):
    running_avg[t] = total_rewards[max(0, t-100):(t+1)].mean()
plt.plot(running_avg)
plt.savefig("plot/r2_pg_rew.png")

# Save video for 1 episode
wrp = gym.wrappers.Monitor(env, "video/r2_pg", force=True)
play_one_ac(wrp, policy, value)
env.close()
wrp.close()


import numpy as np
import matplotlib
import matplotlib.pyplot as plt
from astropy.io import fits
from scipy import signal
import h5py
import copy

# Function: SpatFreqMap
# Description: Builds spatial frequency map for PSD generation (pulled from magaox_poppy code)
# Input Parameters:
#   optic_radius    - radius of primary mirror
#   num_pix         - side length of test region (512 is passed in for MagAO-X)
# Output:
#    magK           - spatial frequency map with proper circular pattern
def SpatFreqMap(optic_radius, num_pix):
    sample_rate = (optic_radius*2)/num_pix

    FT_freq = np.fft.fftfreq(num_pix,d=sample_rate)
    kx = np.resize(FT_freq,(FT_freq.size, FT_freq.size))

    # Build ky the slow way
    y_val=np.reshape(FT_freq,(FT_freq.size,1))
    ky=y_val
    for m in range (0,y_val.size-1):
        ky=np.hstack((ky,y_val))
    magK = np.fft.fftshift(np.sqrt(kx*kx + ky*ky))
    return magK

# Function: calcPower
# Description: Calculate the power of a wavefront (needs better description)
# Input Parameters:
#   wf_data     - wavefront data
#   wavelength  - wavelength used to measure
# Output:
#   power_data  - REAL PART modulous square of the fourier transform with the wavefront data
def calcPower(wf_data, wavelength):
    wavefront = np.exp((2*np.pi/wavelength)*1j*wf_data)
    FT_wf = np.fft.fftshift(np.fft.fft2(wavefront))
    power_data = np.real(FT_wf*np.conjugate(FT_wf))
    return power_data

# Function: calcPower
# Description: Calculate the power of a wavefront, without putting it in exponential (needs better description)
# Input Parameters:
#   wf_data     - wavefront data
# Output:
#   power_data  - REAL PART modulous square of the fourier transform with the wavefront data
def calcPower2(wf_data):
    FT_wf = np.fft.fftshift(np.fft.fft2(wf_data))
    power_data = np.real(FT_wf*np.conjugate(FT_wf))
    return power_data

# Function: getSampSide
# Description: Calculates the sample side based on the largest side of the image
# Input Parameters:
#   optic_data  - 2D array of the data
# Output:
#   samp        - Sample side value
def getSampSide(optic_data):
    # Choose the larger side first
    if np.shape(optic_data)[0] > np.shape(optic_data)[1]:
        samp_side = np.shape(optic_data)[0]
    else:
        samp_side = np.shape(optic_data)[1]

    # Choosing a sample size
    if samp_side < 512:
        if samp_side < 256:
            samp = 256
        else:
            samp = 512
    else:
        samp = 1024

    return samp

# Function: zeroPadSquare
# Description: Zero pad a non-symmetric array into a square array
# Input Parameters:
#   optic_data  - 2D array of data, non-symmetric in size
# Output:
#   sym_data    - zero-padded symmetric array (size is of the larger side)
def zeroPadSquare(optic_data):
    squareMat = copy.copy(optic_data)
    n_row = np.shape(optic_data)[0]
    n_col = np.shape(optic_data)[1]
    side_diff = np.abs(n_row - n_col)
    #print('side difference: %d' % side_diff)

    # check if the difference is odd or even
    if side_diff % 2 == 0:
        odd_diff = False
        #print('The difference is even.')
    else:
        odd_diff = True
        #print('The difference is odd.')

    # count how many times to add row/col on both sides (hence divide by 2)
    count = np.int(np.floor(side_diff/2))
    #print('count = %d' % count)

    # fill in the matrix
    if n_row > n_col: # if too many rows over columns, fill in more columns both sides
        #print('There are more columns than rows')
        filler_row = np.zeros(n_row)[np.newaxis]
        for c in range(0,count):
            #print('entered the filler loop')
            squareMat = np.hstack((filler_row.T,np.hstack((squareMat,filler_row.T))))
        if odd_diff == True: # add one more column on left if odd difference
            squareMat = np.hstack((filler_row.T,squareMat))
        #print('This is the new matrix dimensions: %d, %d' % (np.shape(squareMat)[0], np.shape(squareMat)[1]))

    elif n_col > n_row: # too many columns than rows
        #print('There are more rows than columns')
        filler_col = np.zeros(n_col)
        for c in range(0,count):
            #print('entered the filler loop')
            squareMat = np.vstack((filler_col,np.vstack((squareMat,filler_col))))
        if odd_diff == True:
            squareMat = np.vstack((filler_col,squareMat))
        #print('This is the new matrix dimensions: %d, %d' % (np.shape(squareMat)[0], np.shape(squareMat)[1]))

    return squareMat

# Function: zeroPadOversample
# Description: makes a zero pad based on some oversampling requirements
# Input Parameters:
#   optic_data  - 2D array of the data
#   oversamp    - oversampling multiplier
# Output:
#   zp_wf       - zero padded wavefront
def zeroPadOversample(optic_data,oversamp):
    n_row = np.shape(optic_data)[0]
    n_col = np.shape(optic_data)[1]

    if n_row != n_col: # check if a symmetric matrix is being passed in
        # zero pad data into symmetric matrix
        data = zeroPadSquare(optic_data)
        # recalibrate the number of rows and columns
        n_row = np.shape(data)[0]
        n_col = np.shape(data)[1]
    else:
        data = np.copy(optic_data)
    # Sample the matrix as some 2-factor value
    samp = getSampSide(data)

    # This is the oversampled side size
    side = samp * oversamp
    # NOTE: This will not work for an odd symmetric matrix! If you get an error, this is why.
    row_pad = np.int((side - n_row)/2)
    zp_wf = np.pad(data, (row_pad,row_pad), 'constant')

    return zp_wf

# Function: doCenterCrop
# Description: crops image into square from center of image
# Input Parameters:
#   optic_data  - original 2D optic data, assumes SQUARE data
#   shift       - how many pixels to shift on each side
# Output:
#   crop_data   - cropped data
def doCenterCrop(optic_data,shift):
    side = np.shape(optic_data)[0]
    center = np.int(side/2)
    crop_data = optic_data[center-shift:center+shift,center-shift:center+shift]
    return crop_data

# Function: makeHannWindow
# Description: Builds a 2D Hann window
# Input Paramenters:
#   sideLen     - length of side for Hann window
# Output:
#   hannWin     - 2D (sideLen, sideLen) Hann window
def makeHannWindow(sideLen):
    hannSide = signal.hann(sideLen)
    hannArray = np.tile(hannSide,(sideLen,1)) # tile the rows
    hannWin = hannArray * hannSide[:, np.newaxis] # multiply across each column in tiled row array
    return hannWin

# Function: makeRingMask
# Description: Makes the radial median mask, which looks like a ring.
# Input Parameters:
#   y           - meshgrid vertical values (pixel count units)
#   x           - meshgrid horizontal values (pixel count units)
#   inner_r     - inner radial value (pixel count units)
#   dr          - ring thickness (pixel count units)
# Output:
#   ringMask    - ring mask (boolean type)
def makeRingMask(y,x,inner_r,dr):
    inside_mask = x**2+y**2 <= inner_r**2
    outside_mask = x**2+y**2 <= (inner_r+dr)**2
    ringMask = outside_mask - inside_mask
    return ringMask

# Function: makeRingMaskBin
# Description: Returns the bin of values from the ring mask
# Input Parameters:
#   power_data  - wavefront power data, must be square matrix
#   ringMask    - ring mask, must be same size as power_data and boolean type
# Output:
#   ringMaskBin - vector of values that survived through the mask
def makeRingMaskBin(power_data, ringMask):
    ringMaskBin = np.extract(ringMask, power_data)
    return ringMaskBin

# Function: getRadialSpatFreq
# Description: Determines the spatial frequency value at a radial distance
# Input Parameters:
#   radialFreqVector    - radial frequency in vector format (can do vector since it's radially symmetric
#   r                   - index value for inner radial distance
#   dr                  - radial thickness value
# Output:
#   radialFreq          - radial frequency value
def getRadialSpatFreq(radialFreqVector, r, dr):
    radialFreq = ((radialFreqVector[r+dr] - radialFreqVector[r])/2)+radialFreqVector[r]
    return radialFreq

# Function:calcPSD
# Description: calculates the PSD from a given wavefront data
# Input Parameters:
#   wf_loc              - wavefront data location (string)
#   oversamp            - how big to oversample with zero padding
#   shift               - half length of side to crop (so returned data will be sized 2*shift x 2*shift
# Output:
#   psd_out             - output PSD
def calcPSD(wf_loc, oversamp, shift):
    # Call in wavefront data
    optic_data, optic_header = fits.getdata(wf_loc, header=True)
    # Calculate the PSD value
    optic = zeroPadSquare(optic_data)
    hannWin = makeHannWindow(np.shape(optic)[0])
    optic_win = optic * hannWin
    optic_oversamp = zeroPadOversample(optic_win,oversamp)
    zygo_wavelen = optic_header['WAVELEN']
    power_optic = doCenterCrop(calcPower2(optic_oversamp),shift)
    return power_optic

# Function: getAvgPSD
# Description: Returns the average PSD from a set of PSDs
# Input Parameters:
#   psd_set             - list-type of 2D PSD's (so a 3D list)
# Output:
#   avg_psd             - average PSD
def getAvgPSD(psd_set):
    # convert to numpy array to work numpy from list
    np_psd = np.asarray(psd_set)

    # determine size of each set (each PSD slice should be the same size)
    row_side = np.shape(psd_set)[1]
    col_side = np.shape(psd_set)[2]

    # initialize variable
    avg_psd = np.zeros((row_side, col_side))

    # pixel-by-pixel averaging until Jhen becomes a better programmer
    for row in range(0,row_side):
        for col in range(0,col_side):
            avg_psd[row][col] = np.mean(np_psd[:,row,col])

    return avg_psd

# Function: median_radial_PSD
# Description: Returns 2 lists: median PSD value, and the spatial frequency for that median PSD value
# Input Parameters:
#   psd_set             - list-type of 2D PSD's (so a 3D list)
# Output:
#   avg_psd             - average PSD
def get_median_radial_PSD(avg_psd, shift, dr, radialFreq):
    # Build the radial mask grid
    maskY,maskX = np.ogrid[-shift:shift, -shift:shift]
    r = 1 # skip the center pixel
    # initialize empty lists
    median_val = [] # initialize empty list of median power values
    k_val = [] # initialize empty list of frequencies
    # Calculate through radial mask grid to get median PSD value
    while((r+dr)<(shift)):
        radial_mask = makeRingMask(maskY,maskX,r,dr)
        radial_bin = makeRingMaskBin(avg_psd, radial_mask)
        median_val.append(np.median(radial_bin))
        k_val.append(getRadialSpatFreq(radialFreq,r,dr))
        r = r+1

    return median_val, k_val


#   Old version was PIL
#   Developers kept it as PIL instead of changing it to Pillow
#   Pycharm/Preference/(select the Project)/ProjectInterpreter
#   Click +
#   Search Pillow (if you have it installed on your computer)
#   Import Pillow to project
#   Now we can incorporate it into our project with:
from PIL import *
#   We are using python3.7
#   Making a basic image
#   Make sure to bring the actual image into the project folder.
#       Though you could open via a directory.
#   Note 'img' is our simple made-up title. Not mandatory for an image

# =====================================
from tkinter import *
import tkinter as tk
from tkinter import ttk
from initialWorkingReduction import *
from Functions import *
from embeddSimpleMessage import *

XL_FONT     = ("Verdana", 30)  # Base font that we want to use and will call
LARGE_FONT  = ("Verdana", 18)  # Base font that we want to use and will call
NORM_FONT   = ("Verdana", 12)  # Base font that we want to use and will call
SMALL_FONT  = ("Verdana", 10)  # Base font that we want to use and will call

# Main
if __name__ == "__main__":

    '''The file initialWorkingReduction takes the fox.jpg file,
    and reduces every working pixel by 25%

    This return value here is what the change does, and returns.
    That is also saved.'''
    #img = initialWorking_pixelReduction()
    #img.show()

    img2 = embeddSimpleMessage()
    print("make it here???")
    #img2.show()

    # this encrypts but is symmetric
    symmetricEncryptMessage("xxx")

    '''
    global key
    key = b'Sixteen byte key' # he made this global

    global message
    message = "this is my super secret message"

    from Crypto.Cipher import AES
    global cipher
    cipher = AES.new(key)
    '''
    from globals import message
    print("\n\nMessage: ", message)
    encrypted = encrypt(message)
    decrypted = decrypt(encrypted)
    print("type(decrypted):\t", type(decrypted))
    print("Original Message: ", decrypted)
    print("type(encrypted):\t", type(encrypted))
    print("Encrypted: ", encrypted)

    enc2 = str(encrypted) # Turn encypted into string
    return2 = stringTObinary(enc2) # Convert string into binary
    from globals import joinJoinJoin
    print("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n")
    for x in range(0, len(return2)):
        joinJoinJoin = joinJoinJoin + return2[x]
    print("Pixel Pickup: " + joinJoinJoin)
    print("Decrypt Pickup Placement: ", return2)
    import binascii
    #for y in range(0, len(return2)):
    joinedString = ""
    for y in range(2, len(return2)):
        temp  = chr(int(return2[y], base=2))
        print("temp1 = " + temp)
        if temp != "x":
            joinedString += temp

    print(type(joinedString))
    #joinedString = joinedString.encode()
    joinedString = bytes(joinedString, encoding='utf-8')
    print(type(joinedString))
    print("type(encrypted):\t", type(encrypted))
    print("joinedStr =", joinedString)
    print("Recovered: ", encrypted)
    print("Decrypted: ", decrypted)
    #decrypted = decrypt(joinedString)
    #print("type(decrypted):\t", type(decrypted))
    #print("Decrypted: ", decrypted)

    #bbb = b'gAAAAABb4nSALA0pvaISSbPzcQ7Tv6YIafMje7wvqeWGfq9SsakmuwqncNvraR8c5bdfChc9BqPjJsB35dXdzr7IxY-hvsi2-XwmWnoe8thPQnaLB3_QTTJpGZGBwl3XLl3-jb8lBp46'
    #bytesTOstring(bbb)
    '''
    initial byteMessage:  b'gAAAAABb4nSALA0pvaISSbPzcQ7Tv6YIafMje7wvqeWGfq9SsakmuwqncNvraR8c5bdfChc9BqPjJsB35dXdzr7IxY-hvsi2-XwmWnoe8thPQnaLB3_QTTJpGZGBwl3XLl3-jb8lBp46'
    byteTOstring post decode:  gAAAAABb4nSALA0pvaISSbPzcQ7Tv6YIafMje7wvqeWGfq9SsakmuwqncNvraR8c5bdfChc9BqPjJsB35dXdzr7IxY-hvsi2-XwmWnoe8thPQnaLB3_QTTJpGZGBwl3XLl3-jb8lBp46
    '''

    #we have AES encryption and decryption in functions

    #returnedBinary = stringTObinary("Nate is great")
    '''
    stringMessage:  Nate is great
    binaryMessage:  ['01001110', '01100001', '01110100', '01100101', '00100000', '01101001', '01110011', '00100000', '01100111', '01110010', '01100101', '01100001', '01110100']
    '''

            # ===============if RGB - end=================================
        # =============for x - end============================================
    # =================for y - end============================================
                # ============================================================
                # NOTE - this is what is being printed to our file
                # Based on our if/or, it is only printed if the pixel is not White
                # We are moving from top to bottom, then from left to right in each row,
                #     ===========================
                #          x, y = (313, 200)
                #          R = 254	G = 254	B = 254
                #          ===========================
                #          x, y = (314, 200)
                #          R = 253	G = 253	B = 253
                #          ===========================
                #          x, y = (315, 200)
                #          R = 254	G = 254	B = 254
                #          ===========================
                #          x, y = (318, 200)
                #          R = 254	G = 254	B = 254
                #          ===========================
                #break # note - break is just here to have a result in our if statement
                # Since we are writing to file in the if statement, we don't need a break
    '''
    # =============================================================
    # =============================================================
    # =============================================================
    # =============================================================
    # =============================================================
    # =============================================================
    # DETAILS FOR WORKING WITH TXT FILES.==========================
    #       file = open("copy.txt", "w")
    #       file.write("Your text goes here")
    #       file.close()
    #       The default value is r and will fail if the file does not exist

    #       'r' open for reading (default)
    #       'w' open for writing, truncating the file first
    #       Other interesting options are

    #       'x' open for exclusive creation, failing if the file already exists
    #       'a' open for writing, appending to the end of the file if it exists
    # =============================================================
    # =============================================================
    # =============================================================

    # ============================================
    # ============================================
    # ============================================
    '''
    '''
    # Load Image (JPEG/JPG needs libjpeg to load)
    original = open_image('Apple_original.jpg')

    # Example Pixel Color
    print('Color: ' + str(get_pixel(original, 0, 0)))

    # Convert to Grayscale and save
    # Working
    new = convert_grayscale(original)
    save_image(new, 'Prinny_gray.png')

    # Convert to Primary and save
    new = convert_primary(original)
    save_image(new, 'Prinny_primary.png')
    '''


"""
funciones.py
Módulo que implementa las siguientes funciones:

-get_data: Esta función obtiene datos de la red en un rango de tiempo
especifico y los almacena en listas
-set_data: Ordena los datos ingresados en una lista de diccionarios
"""

import requests
from bs4 import BeautifulSoup

def get_data(dq_dia, dq_mes, aq_dia, aq_mes, aq_anio, data_limit):
    """
    Esta función obtiene datos de la red en un rango de tiempo
    especifico y los almacena en listas

    Parameters
    ----------
    dqDia : string
        Valor del día en que comenzara la busqueda\n
    dqMes : string
        Valor asociado al mes en el que comenzara la busqueda\n
    aqDia : string
        Valor del día en que terminara la busqueda\n
    aqMes : string
        Valor asociado al mes en el que terminara la busqueda\n
    aqAnio : string
        Valor del año en el que se realizara la busqueda\n
    data_limit : int
    Número de datos a obtener por cada tipo de dato
    (string y float)

    Returns
    ----------
    data_string : list
        Lista con los datos almacenados en formato de cadena\n
    data_float : list
        Lista con los datos almacenados en formato de flotante

    Raises
    ----------
    ValueError
        Si no se pudo conectar al servidor
    """

    #Listas donde se almacenarán los datos obtenidos
    data_string=[]
    data_float=[]

    #Parametros para contruir el url del que obtendremos información
    params={"Punto": 100, "dqDia": dq_dia, "dqMes":dq_mes, "dqAnio": aq_anio,
            "aqDia": aq_dia, "aqMes": aq_mes, "aqAnio": aq_anio}
    url="http://www.economia-sniim.gob.mx/Consolidados.asp"
    response=requests.get(url, params=params)

    #Se obtienen los datos de interes, para ser guardados en listas
    soup = BeautifulSoup(response.content, 'html.parser')
    soup_strings=soup.find_all('td', {'class': 'Datos'}, limit= data_limit)
    soup_floats=soup.find_all('td', {'class': 'TotRes'}, limit= data_limit)
    for i in soup_strings: data_string.append(i.text)
    for i in soup_floats: data_float.append(float(i.text))

    return data_string, data_float

def set_data(new_flt, new_str, old_flt, old_str, data_limit):
    """
    Esta función ordena los datos ingresados en una lista de diccionarios

    Parameters
    ----------
    old_str : list
        Lista con los valores tipo cadena del 2021\n
    old_flt : list
        Lista con los valores tipo flotante del 2021\n
    new_str : list
        Lista con los valores tipo cadena del 2022\n
    new_flt : list
        Lista con los valores tipo flotante del 2022
    data_limit : int
        Número de datos a obtener por cada tipo de dato
    (string y float)

    Returns
    ----------
    final_list : list
        Lista de directorios con los datos del producto
    """
    #Listas donde se almacenarán los directorios d datos
    final_list=[]

    i=0
    while i < data_limit:

        #Flitro de datos para comparar los datos de ambos años
        if (new_flt[i] < old_flt[i]):
            old_flt[i] = new_flt[i]

        if (new_flt[i+1] > old_flt[i+1]):
            old_flt[i+1] = new_flt[i+1]

        if (old_str[i+1] != new_str[i+1]):
            old_str[i+1] = old_str[i+1] + new_str[i+1]

        old_str[i+2] = old_str[i+2] + new_str[i+2]

        #Se obtiene la varicación de los promedios en los 2 años
        var_tiem = old_flt[i+2] - new_flt[i+2]
        old_flt[i+2] = (old_flt[i+2]*365 + new_flt[i+2]*212) / 577

        final_list.append({'product': old_str[i].replace("\n","").replace("\xa0", ""),
                         'min_price': old_flt[i],
                         'max_price': old_flt[i+1],
                         'avg_price': round(old_flt[i+2],2),
                         'origin': old_str[i+1],
                         'distr': old_str[i+2],
                         'var_tiem': var_tiem})

        i=i+3
    return final_list


print('밑 : ' , end= '')
x = int(input())
print('지수 : ', end = '')
y = int(input())

def exp(a, n):

    if n == 0:
        return 1

    else:
        return a * exp(a,n-1)

print(x, '^' , y, '=', exp(x,y))


import math
import torch
from torch.optim import Optimizer
from torch.optim.optimizer import required

class SGD(Optimizer):
    r"""Implements stochastic gradient descent (optionally with momentum).
    Nesterov momentum is based on the formula from
    `On the importance of initialization and momentum in deep learning`__.
    Args:
        params (iterable): iterable of parameters to optimize or dicts defining
            parameter groups
        lr (float): learning rate
        momentum (float, optional): momentum factor (default: 0)
        weight_decay (float, optional): weight decay (L2 penalty) (default: 0)
        dampening (float, optional): dampening for momentum (default: 0)
        nesterov (bool, optional): enables Nesterov momentum (default: False)
    """

    def __init__(self, params, lr=required, momentum=0, dampening=0,
                 sigma=0.0, weight_decay=0, nesterov=False):
        if lr is not required and lr < 0.0:
            raise ValueError("Invalid learning rate: {}".format(lr))
        if momentum < 0.0:
            raise ValueError("Invalid momentum value: {}".format(momentum))
        if weight_decay < 0.0:
            raise ValueError("Invalid weight_decay value: {}".format(weight_decay))

        defaults = dict(lr=lr, momentum=momentum, dampening=dampening, sigma=sigma,
                        weight_decay=weight_decay, nesterov=nesterov)
        if nesterov and (momentum <= 0 or dampening != 0):
            raise ValueError("Nesterov momentum requires a momentum and zero dampening")
        super(SGD, self).__init__(params, defaults)

    def __setstate__(self, state):
        super(SGD, self).__setstate__(state)
        for group in self.param_groups:
            group.setdefault('nesterov', False)

    def step(self, closure=None):
        """Performs a single optimization step.
        Arguments:
            closure (callable, optional): A closure that reevaluates the model
                and returns the loss.
        """
        loss = None
        if closure is not None:
            loss = closure()

        for group in self.param_groups:
            weight_decay = group['weight_decay']
            momentum = group['momentum']
            dampening = group['dampening']
            nesterov = group['nesterov']

            for p in group['params']:
                if p.grad is None:
                    continue
                d_p = p.grad.data

                if weight_decay != 0:
                    d_p.add_(weight_decay, p.data)

                # noise addition
                sigma = group['sigma']
                if sigma > 0.0:
                    d_p = d_p + sigma * torch.randn(d_p.size(),
                                                    device=d_p.device)

                d_p.mul_(group['lr'])

                if momentum != 0:
                    param_state = self.state[p]
                    if 'momentum_buffer' not in param_state:
                        buf = param_state['momentum_buffer'] = torch.clone(d_p).detach()
                        buf.mul_(momentum).add_(d_p)
                    else:
                        buf = param_state['momentum_buffer']
                        buf.mul_(momentum).add_(1 - dampening, d_p)
                    if nesterov:
                        d_p = d_p.add(momentum, buf)
                    else:
                        d_p = buf

                p.data.add_(-1, d_p)

        return loss

class Adam(Optimizer):
    """
    Implements DP-Adam algorithm.

    Arguments:
        params (iterable): iterable of parameters to optimize or dicts defining
            parameter groups
        lr (float, optional): learning rate (default: 1e-3)
        betas (Tuple[float, float], optional): coefficients used for computing
            running averages of gradient and its square (default: (0.9, 0.999))
        eps (float, optional): term added to the denominator to improve
            numerical stability (default: 1e-8)
        weight_decay (float, optional): weight decay (L2 penalty) (default: 0)
        amsgrad (boolean, optional): whether to use the AMSGrad variant of this
            algorithm from the paper `On the Convergence of Adam and Beyond`_
            (default: False)
    .. _Adam\: A Method for Stochastic Optimization:
        https://arxiv.org/abs/1412.6980
    .. _On the Convergence of Adam and Beyond:
        https://openreview.net/forum?id=ryQu7f-RZ
    """

    def __init__(self, params, lr=1e-3, betas=(0.9, 0.999), eps=1e-8,
                 weight_decay=0, sigma=0.0, amsgrad=False):
        if not 0.0 <= lr:
            raise ValueError("Invalid learning rate: {}".format(lr))
        if not 0.0 <= eps:
            raise ValueError("Invalid epsilon value: {}".format(eps))
        if not 0.0 <= betas[0] < 1.0:
            raise ValueError("Invalid beta parameter at index 0: {}".format(betas[0]))
        if not 0.0 <= betas[1] < 1.0:
            raise ValueError("Invalid beta parameter at index 1: {}".format(betas[1]))
        if sigma < 0.0:
            raise ValueError("Invalid sigma value: {}".format(sigma))
        defaults = dict(lr=lr, betas=betas, eps=eps,
                        weight_decay=weight_decay, sigma=sigma,
                        amsgrad=amsgrad)
        super(Adam, self).__init__(params, defaults)

    def __setstate__(self, state):
        super(Adam, self).__setstate__(state)
        for group in self.param_groups:
            group.setdefault('amsgrad', False)

    def step(self, closure=None):
        """Performs a single optimization step.
        Arguments:
            closure (callable, optional): A closure that reevaluates the model
                and returns the loss.
        """
        loss = None
        if closure is not None:
            loss = closure()

        for group in self.param_groups:
            for p in group['params']:
                if p.grad is None:
                    continue
                grad = p.grad.data

                # noise addition
                sigma = group['sigma']
                if sigma > 0.0:
                    # grad = grad + sigma * torch.randn(grad.size(),
                    #                                   device=grad.device)
                    grad.add_(torch.empty(grad.size(),
                                          device=grad.device).normal_(mean=0, std=sigma))

                if grad.is_sparse:
                    raise RuntimeError('Adam does not support sparse gradients, please consider SparseAdam instead')
                amsgrad = group['amsgrad']

                state = self.state[p]

                # State initialization
                if len(state) == 0:
                    state['step'] = 0
                    # Exponential moving average of gradient values
                    state['exp_avg'] = torch.zeros_like(p.data)
                    # Exponential moving average of squared gradient values
                    state['exp_avg_sq'] = torch.zeros_like(p.data)
                    if amsgrad:
                        # Maintains max of all exp. moving avg. of sq. grad. values
                        state['max_exp_avg_sq'] = torch.zeros_like(p.data)

                exp_avg, exp_avg_sq = state['exp_avg'], state['exp_avg_sq']
                if amsgrad:
                    max_exp_avg_sq = state['max_exp_avg_sq']
                beta1, beta2 = group['betas']

                state['step'] += 1

                if group['weight_decay'] != 0:
                    grad = grad.add_(p, alpha=group['weight_decay'])

                # Decay the first and second moment running average coefficient
                exp_avg.mul_(beta1).add_(grad, alpha=1 - beta1)
                exp_avg_sq.mul_(beta2).addcmul_(grad, grad, value=1 - beta2)
                if amsgrad:
                    # Maintains the maximum of all 2nd moment running avg. till now
                    torch.max(max_exp_avg_sq, exp_avg_sq, out=max_exp_avg_sq)
                    # Use the max. for normalizing running avg. of gradient
                    denom = max_exp_avg_sq.sqrt().add_(group['eps'])
                else:
                    denom = exp_avg_sq.sqrt().add_(group['eps'])

                bias_correction1 = 1 - beta1 ** state['step']
                bias_correction2 = 1 - beta2 ** state['step']
                step_size = group['lr'] * math.sqrt(bias_correction2) / bias_correction1

                p.data.addcdiv_(exp_avg, denom, value=-step_size)

        return loss

class RMSprop(Optimizer):
    r"""Implements RMSprop algorithm.
    Proposed by G. Hinton in his
    `course <http://www.cs.toronto.edu/~tijmen/csc321/slides/lecture_slides_lec6.pdf>`_.
    The centered version first appears in `Generating Sequences
    With Recurrent Neural Networks <https://arxiv.org/pdf/1308.0850v5.pdf>`_.
    The implementation here takes the square root of the gradient average before
    adding epsilon (note that TensorFlow interchanges these two operations). The effective
    learning rate is thus :math:`\alpha/(\sqrt{v} + \epsilon)` where :math:`\alpha`
    is the scheduled learning rate and :math:`v` is the weighted moving average
    of the squared gradient.
    Arguments:
        params (iterable): iterable of parameters to optimize or dicts defining
            parameter groups
        lr (float, optional): learning rate (default: 1e-2)
        momentum (float, optional): momentum factor (default: 0)
        alpha (float, optional): smoothing constant (default: 0.99)
        eps (float, optional): term added to the denominator to improve
            numerical stability (default: 1e-8)
        centered (bool, optional) : if ``True``, compute the centered RMSProp,
            the gradient is normalized by an estimation of its variance
        weight_decay (float, optional): weight decay (L2 penalty) (default: 0)
    """

    def __init__(self, params, lr=1e-2, alpha=0.99, eps=1e-8, weight_decay=0, momentum=0,
                 sigma=0.0, centered=False):
        if not 0.0 <= lr:
            raise ValueError("Invalid learning rate: {}".format(lr))
        if not 0.0 <= eps:
            raise ValueError("Invalid epsilon value: {}".format(eps))
        if not 0.0 <= momentum:
            raise ValueError("Invalid momentum value: {}".format(momentum))
        if not 0.0 <= weight_decay:
            raise ValueError("Invalid weight_decay value: {}".format(weight_decay))
        if not 0.0 <= alpha:
            raise ValueError("Invalid alpha value: {}".format(alpha))
        if sigma < 0.0:
            raise ValueError("Invalid sigma value: {}".format(sigma))
        defaults = dict(lr=lr, momentum=momentum, alpha=alpha, eps=eps, centered=centered,
                        sigma=sigma, weight_decay=weight_decay)
        super(RMSprop, self).__init__(params, defaults)

    def __setstate__(self, state):
        super(RMSprop, self).__setstate__(state)
        for group in self.param_groups:
            group.setdefault('momentum', 0)
            group.setdefault('centered', False)

    def step(self, closure=None):
        """Performs a single optimization step.
        Arguments:
            closure (callable, optional): A closure that reevaluates the model
                and returns the loss.
        """
        loss = None
        if closure is not None:
            loss = closure()

        for group in self.param_groups:
            for p in group['params']:
                if p.grad is None:
                    continue
                grad = p.grad.data

                # noise addition
                sigma = group['sigma']
                if sigma > 0.0:
                    grad = grad + sigma * torch.randn(grad.size(),
                                                      device=grad.device)

                if grad.is_sparse:
                    raise RuntimeError('RMSprop does not support sparse gradients')
                state = self.state[p]

                # State initialization
                if len(state) == 0:
                    state['step'] = 0
                    state['square_avg'] = torch.zeros_like(p.data)
                    if group['momentum'] > 0:
                        state['momentum_buffer'] = torch.zeros_like(p.data)
                    if group['centered']:
                        state['grad_avg'] = torch.zeros_like(p.data)

                square_avg = state['square_avg']
                alpha = group['alpha']

                state['step'] += 1

                if group['weight_decay'] != 0:
                    grad = grad.add(group['weight_decay'], p.data)

                square_avg.mul_(alpha).addcmul_(1 - alpha, grad, grad)

                if group['centered']:
                    grad_avg = state['grad_avg']
                    grad_avg.mul_(alpha).add_(1 - alpha, grad)
                    avg = square_avg.addcmul(-1, grad_avg, grad_avg).sqrt_().add_(group['eps'])
                else:
                    avg = square_avg.sqrt().add_(group['eps'])

                if group['momentum'] > 0:
                    buf = state['momentum_buffer']
                    buf.mul_(group['momentum']).addcdiv_(grad, avg)
                    p.data.add_(-group['lr'], buf)
                else:
                    p.data.addcdiv_(-group['lr'], grad, avg)

        return loss


import numpy as np

def ac_lista(df,index):
     df_ac = df[df['index_NSP']==index][['accelerations']].value_counts()
     val_index = [df_ac.iloc[0],df_ac.iloc[1:].sum()]
     return val_index

def lista_NSP(colores,index):
    lista = []
    i=0
    while i <16:
        lista.append(colores[index])
        i+=1
    return lista

def porcentaje(df,var):
    lista = []
    for n in np.arange(5):
        i = 0
        while i<3:
            if len(df[df[var]==(n+1)].groupby('index_NSP')[['index_NSP']].count())==1:
                lista.append(100)
                lista.append(0)
                lista.append(0)
                break
            if len(df[df[var]==(n+1)].groupby('index_NSP')[['index_NSP']].count())==2:
                lista.append(5.71)
                lista.append(0)
                lista.append(94.29)
                break
            else:
                total = df[df[var]==(n+1)].groupby('index_NSP').count().sum()['abnormal_STV']
                num = df[df[var]==(n+1)].groupby('index_NSP').count().iloc[i]['abnormal_STV']
                lista.append((num*100/total).round(2))
            i+=1
    return lista

def class_STV(df):
    if df['abnormal_STV']<18:
        val = 1
    elif df['abnormal_STV']<36:
        val = 2
    elif df['abnormal_STV']<54:
        val = 3
    elif df['abnormal_STV']<72:
        val = 4
    else:
        val = 5
    return val

def class_LTV(df):
    if df['abnormal_LTV']<18:
        val = 1
    elif df['abnormal_LTV']<36:
        val = 2
    elif df['abnormal_LTV']<54:
        val = 3
    elif df['abnormal_LTV']<72:
        val = 4
    else:
        val = 5
    return val