# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, x):
#         self.val = x
#         self.left = None
#         self.right = None

class Solution:
    def maxVal(self, root: TreeNode):
        max_val = root.val
        if root.left is not None:
            max_val = max(max_val, self.maxVal(root.left))
        if root.right is not None:
            max_val = max(max_val, self.maxVal(root.right))
        return max_val
    def minVal(self, root: TreeNode):
        min_val = root.val
        if root.left is not None:
            min_val = min(min_val, self.maxVal(root.left))
        if root.right is not None:
            min_val = min(min_val, self.maxVal(root.right))
        return min_val
    def isValidBST(self, root: TreeNode) -> bool:
        if root is None:
            return True
        if root.left is not None:
            if not self.maxVal(root.left) < root.val:
                return False
            if not self.isValidBST(root.left):
                return False
        if root.right is not None:
            if not root.val < self.minVal(root.right):
                return False
            if not self.isValidBST(root.right):
                return False
        return True


"""A location inference model that matches entities with a gazetteer."""
import functools as ft
from typing import Any, Dict, List, Optional, Set, Tuple

import pandas as pd
from scipy.special import softmax
from spacy.lang.en import English
from tqdm import tqdm

from src.models.__init__ import get_ents, get_user_spacy, DENYLIST
from src.schema import Location, Post, User
from src.utils import connect_to_mongo, get_nlp

from .filters import BaseFilter, DenylistFilter, LocationFilter
from .rankers import BaseRanker, FrequencyRanker

# -- HELPER FUNCTIONS --

def rankings_averager(gpe: str, rankings: List[Dict[str, float]]) -> float:
    """Combine rankings for a given gpe by averaging each rankings respective score."""
    scores = [r[gpe] for r in rankings]
    return sum(scores) / len(scores)

def load_locations(fp: str) -> Set[Location]:
    """
    Retrieve US city/county/state locations from a csv filepath.
    """
    df = pd.read_csv(fp)

    def string_or_none(maybe_string: Any) -> Optional[str]:
        return maybe_string if type(maybe_string) == str else None

    def row_to_loc(row: pd.Series) -> Location:
        return Location(neighborhood=string_or_none(row["neighborhood"]),
                        city=string_or_none(row["city"]),
                        county=string_or_none(row["county"]),
                        state=string_or_none(row["state"]),
                        country=string_or_none(row["country"]),
                        metro=string_or_none(row["metro"]))

    return set([row_to_loc(row) for _, row in df.iterrows()])

def find_parent_location(record_1: Location,
                         record_2: Location) -> List[Location]:
    """
    If a parent exists, return a list just containing that parent.
    Else, return the two records.
    """
    parent = Location()
    if record_1["country"] == record_2["country"]:
        parent["country"] = record_1["country"]
        if record_1["state"] == record_2["state"]:
            parent["state"] = record_1["state"]
            if record_1["county"] == record_2["county"]:
                parent["county"] = record_1["county"]
                if record_1["city"] == record_2["city"]:
                    parent["city"] = record_1["city"]

    if parent.country:
        return [parent]
    return [record_1, record_2]

def group_locations(locations: Set[Location]) -> List[Location]:
    """Find a common parent for the records if possible."""

    for location in locations:
        location.neighborhood = None

    return list(set(locations))

def group_locations_by_score(
    records: List[Location], scores: List[float]
) -> Tuple[List[Location], List[float]]:
    """Attempt to find a common location parent for locations with the same score."""
    # group records with same score
    score_records_map = dict()
    for record, score in zip(records, scores):
        if score in score_records_map:
            score_records_map[score].append(record)
        else:
            score_records_map[score] = [record]

    # reduce records for each score that have a common ancestor
    new_records = []
    new_scores = []
    for score in score_records_map:
        records = score_records_map[score]
        grouped_records = group_locations(set(records))
        new_records = new_records + grouped_records
        new_scores = new_scores + [score] * len(grouped_records)

    return new_records, new_scores

def group_by_metro(location_scores: Dict[Location, float]) -> Dict[Location, float]:
    # sum scores for each metro area
    metro_scores = {l.metro: 0.0 for l in location_scores}
    for loc in location_scores:
        score = location_scores[loc]
        metro_scores[loc.metro] += score

    def metro_to_location(metro: str) -> Optional[Location]:
        """Helper func to generate full location for a given metro"""
        for location in location_scores:
            if location.metro == metro:
                return Location(metro=metro,
                                state=location.state,
                                country=location.country)
        return None

    # convert metro areas to locations
    new_location_scores: Dict[Location, float] = dict()
    for metro in metro_scores:
        if type(metro) == str:
            # if metro area exists, convert metro area to a location
            new_location_scores[metro_to_location(metro)] = metro_scores[metro]
        else:
            # otherwise, add each location that doesn't have a metro area with its score
            non_metro_locs = [l for l in location_scores if not l.metro]
            for l in non_metro_locs:
                new_location_scores[l] = location_scores[l]

    return new_location_scores

# -- MODEL DECLARATION --

class LocationMatcher:
    def __init__(self, filters: List[BaseFilter], rankers: List[BaseRanker],
                 real_locations: pd.DataFrame, nlp: English):
        self.filters = filters
        self.rankers = rankers
        self.real_locations = real_locations
        self.nlp = nlp

    def score_entities(self, user: User) -> Dict[str, float]:
        """Assign a likeliness score to each location entity in a user's posting history."""
        # extract user locations
        spacy_docs = get_user_spacy(user, self.nlp)
        gpes = get_ents(spacy_docs, "GPE")

        # apply filters
        possible_gpes = ft.reduce(lambda acc, f: f.filter(acc), self.filters,
                                  set(gpes))
        filtered_gpes = [gpe for gpe in gpes if gpe in possible_gpes]

        # rank gpes with different methods
        rankings = [r.rank(filtered_gpes) for r in self.rankers]

        # combine rankings
        scores = {
            gpe: rankings_averager(gpe, rankings)
            for gpe in possible_gpes
        }

        return scores

    def score_locations(
            self,
            entity_scores: Dict[str, float]
    ) -> List[Tuple[Location, float]]:
        """
        Convert each location entity in a user's posting history to a real location.
        Then, assign a likeliness score to each location.
        """
        records = self.real_locations.to_dict(orient="records")

        guessed_records = []
        scores = []
        for record in records:
            score = 0.0
            for field in record.values():
                if field in entity_scores:
                    score += entity_scores[field]
            if score > 0:
                guessed_records.append(record)
                scores.append(score)

        guessed_locations = [Location(**record) for record in guessed_records]

        metro_scores = group_by_metro({
            l: s for l, s in zip(guessed_locations, scores)
        })

        normalized_scores = softmax(list(metro_scores.values())) if len(metro_scores) > 0 else []

        location_scores = {
            location: score
            for location, score in zip(list(metro_scores.keys()), normalized_scores)
        }

        return sorted(location_scores.items(),
                      key=lambda _: _[1],
                      reverse=True)

    def _predict(self, location_scores: Dict[Location, float]) -> Optional[Location]:
        """
        Compute likeliness scores for each entity in a user's posting history and
        package them into a single most likely location.
        """
        if len(location_scores) > 0:
            max_loc = sorted(location_scores.items(),
                             key=lambda _: _[1],
                             reverse=True)[0]
            return max_loc
        return None

if __name__ == "__main__":

    print("Connecting to mongo .....")
    connect_to_mongo()

    # define filters/rankers
    fp = "resources/grouped-locations.csv"
    locations = pd.read_csv(fp)
    filters = [DenylistFilter(DENYLIST), LocationFilter(locations)]
    rankers = [FrequencyRanker()]
    nlp = get_nlp()

    # instantiate model
    model = LocationMatcher(filters, rankers, locations, nlp)

    use_top = True
    print("Getting users .....")

    if use_top:
        # get the users with the most posts
        pipeline = [{"$sortByCount": "$user"}, {"$limit": 5}]
        res = Post.objects().aggregate(pipeline)
        ids = [str(r["_id"]) for r in res if r["_id"]]
        users = User.objects(id__in=ids)
    else:
        usernames = pd.read_csv("clutter/rand_user_200.csv",
                                squeeze=True).tolist()
        users = User.objects(username__in=usernames).all()

    print("Making predictions .....")
    scores = [model.score_locations(user) for user in tqdm(users)]
    breakpoint()


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

# In[25]:

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import matplotlib.ticker as ticker
import matplotlib.ticker as plticker
from sklearn.model_selection import train_test_split
from sklearn.linear_model import LogisticRegression

# In[26]:

ucl_data = pd.read_csv('ucl_data.csv')
ucl_data.head()

# In[27]:

winner = []
for i in range (len(ucl_data['Team'])):
    if ucl_data ['Goalf'][i] > ucl_data['Goala'][i]:
        winner.append(ucl_data['Team'][i])
    else:
        winner.append('other')
ucl_data['winning'] = winner

ucl_data.head()

# In[28]:

Homeg = []
Away = []
for i in range (len(ucl_data['Team'])):
    if ucl_data ['Home'][i] == 1:
        Homeg.append(ucl_data['Team'][i])
        Away.append('Other')
    else:
        Homeg.append('Other')
        Away.append(ucl_data['Team'][i])
ucl_data['Homeg'] = Homeg
ucl_data['Away'] = Away

ucl_data.head()

# In[29]:

matches_data = ucl_data
matches_data

# In[30]:

matches_data = matches_data.reset_index(drop=True)
matches_data.loc[matches_data.winning == matches_data.Homeg,'winning']=2
matches_data.loc[matches_data.winning == matches_data.Away,'winning']=1
matches_data.loc[matches_data.winning == 'other','winning']=0

matches_data

# In[31]:

matches_data = matches_data.drop(['Team'], axis=1)
final = pd.get_dummies(matches_data, prefix=['Homeg', 'Away'], columns=['Homeg', 'Away'])

X = final.drop(['winning'], axis=1)
y = final["winning"]
y = y.astype('int')

X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.30, random_state=42)

# In[32]:

final.head()

# In[33]:

result_model = LogisticRegression()
result_model.fit(X_train, y_train)
score = result_model.score(X_train, y_train)
score2 = result_model.score(X_test, y_test)

print("Training set accuracy: ", '%.3f'%(score))
print("Test set accuracy: ", '%.3f'%(score2))

# In[34]:

ranking = pd.read_csv('ranking.csv')
Rounds = pd.read_csv('Rounds.csv')

pred_set = []

# In[35]:

Rounds.insert(1, 'first_position', Rounds['Homeg'].map(ranking.set_index('Team')['position']))
Rounds.insert(2, 'second_position', Rounds['Away'].map(ranking.set_index('Team')['position']))

Rounds.tail()

# In[36]:

for index, row in Rounds.iterrows():
    if row['first_position'] < row['second_position']:
        pred_set.append({'Homeg': row['Homeg'], 'Away': row['Away'], 'winning': None})
    else:
        pred_set.append({'Homeg': row['Away'], 'Away': row['Homeg'], 'winning': None})

pred_set = pd.DataFrame(pred_set)
backup_pred_set = pred_set

pred_set.head()

# In[37]:

pred_set = pd.get_dummies(pred_set, prefix=['Homeg', 'Away'], columns=['Homeg', 'Away'])

missing_cols = set(final.columns) - set(pred_set.columns)
for c in missing_cols:
    pred_set[c] = 0
pred_set = pred_set[final.columns]

pred_set = pred_set.drop(['winning'], axis=1)

pred_set.head()

# In[38]:

predictions = result_model.predict(pred_set)
count = 1
for i in range(Rounds.shape[0]):
    num = 1
    print("Match" +str(count)+ ": " + backup_pred_set.iloc[i, 1] + " VS " + backup_pred_set.iloc[i, 0])
    if predictions[i] == 1:
        print("Winner: " + backup_pred_set.iloc[i, 1])
    elif predictions[i] == 0:
       # print("Penalties")
        if '%.3f'%(result_model.predict_proba(pred_set)[i][2]) > '%.3f'%(result_model.predict_proba(pred_set)[i][0]):
            print( backup_pred_set.iloc[i, 1] + ' winning: ', '%.3f'%(result_model.predict_proba(pred_set)[i][2]))
        else:
            print( "Winner: "+backup_pred_set.iloc[i, 0] )
        num = 0
    elif predictions[i] == 2:
        print("Winner: " + backup_pred_set.iloc[i, 0])
    print("")
    count = count+1

# In[ ]:


import json
import click
from enum import Enum

from flask import Flask

app = Flask(__name__)

json_resume = 'resume.jjson'

class ApplicationError(Exception):
    code = 'default'
    def __init__(self, message):
        self.message = message

class JsonSyntaxError(ApplicationError):
    code = 'json.syntax.error'

class ResumeFileNotFound(ApplicationError):
    code = 'resume.file.not.found'

class Chapters(Enum):
    PERSONAL = 'personal'
    EXPERIENCE = 'experience'
    EDUCATION = 'education'
    ALL = 'all'

def get_resume_json(chapter: Chapters):
    try:
        with open(json_resume, 'r') as f:
            data = json.loads(f.read())
    except json.decoder.JSONDecodeError as e:
        raise JsonSyntaxError('JOSN resume syntax error: ' + str(e))
    except FileNotFoundError as e:
        raise ResumeFileNotFound(f'File {json_resume} does not exist.')

    rv = data if Chapters(chapter) == Chapters.ALL else data.get(chapter.value)
    return rv or {}

@app.errorhandler(404)
def handle_not_found_error(error):
    response = {
        'message': 'Resource not found',
        'error_code': '404.not.found'
    }
    return response, 404

@app.after_request
def add_status(response):
    content_type = response.headers.get('Content-Type')
    if content_type != 'application/json':
        return response

    data = json.loads(response.data)

    enhanced_data = {
        'status': 'success' if not data.get('error_code') else 'failed',
        'data': data.get('data', {}),
        'error_code': data.get('error_code', None),
        'message': data.get('message', None)
    }
    response.data = json.dumps(enhanced_data)
    if enhanced_data.get('error_code') and response.status.startswith('2'):
        response.status = 500
    return response

def api_error_catcher(func):
    def wrapper(*args, **kwargs):
        try:
            rv = func(*args, **kwargs)
        except ApplicationError as e:
            return {
                'error_code': e.code,
                'message': e.message
            }
        return rv
    return wrapper

@app.route("/personal", methods=['GET'], endpoint='get_personal')
@api_error_catcher
def get_personal():
    return {'data': get_resume_json(Chapters.PERSONAL)}

@app.route("/experience", methods=['GET'], endpoint='get_experience')
@api_error_catcher
def get_experience():
    return {'data': get_resume_json(Chapters.EXPERIENCE)}

@app.route("/education", methods=['GET'], endpoint='get_education')
@api_error_catcher
def get_education():
    return {'data': get_resume_json(Chapters.EDUCATION)}

@app.cli.command()
@click.argument('chapter', type=Chapters)
def get_chapter(chapter: Chapters):
    """Get a chapter of the resume.

    Example: flask get-chapter experience

    CHAPTER is the part of the resume that you want returned: personal,
    education, experience, all.
    """
    click.echo(json.dumps(get_resume_json(chapter), indent=4))

@app.cli.command()
@click.option('--personal', is_flag=True, help='Set flag to get the personal chapter')
@click.option('--experience', is_flag=True, help='Set flag to get the experience chapter')
@click.option('--education', is_flag=True, help='Set flag to get the education chapter')
def compose_resume(personal, experience, education):
    """Add the chapters by setting the flags that interest you.

    Example: flask compose-resume --education --personal
    """

    resume = {}
    mapping = {
        Chapters.PERSONAL: personal,
        Chapters.EXPERIENCE: experience,
        Chapters.EDUCATION: education,
    }
    try:
        for chapter in filter(lambda x: mapping[x], mapping):
            resume[chapter.value] = get_resume_json(Chapters(chapter))
    except ApplicationError as e:
        raise click.ClickException(e.message)

    click.echo(json.dumps(resume, indent=4))


import socket
import os

def client():
    host = "3.122.246.149"   # as both code is running on same pc
    port = 5000  # socket server port number

    client_socket = socket.socket()  # instantiate
    client_socket.connect((host, port))  # connect to the server

    message = input(" -> ")  # take input

    while message.lower().strip() != 'bye':
        client_socket.send(message.encode())
        data = client_socket.recv(1024).decode()

        print('Received from server: ' + data)

        message = input(" -> ")

    client_socket.close()

if __name__ == '__main__':
    client()


"""
Support for Control4 devices

For more details about this component, please refer to the documentation at
https://github.com/r3pi/homeassistant-control4
"""

import logging
import voluptuous as vol

from homeassistant.helpers import config_validation as cv
from homeassistant.helpers.aiohttp_client import async_get_clientsession

from .const import DOMAIN

_LOGGER = logging.getLogger(__name__)

CONF_URL = 'url'
CONF_PROXY = 'proxy'

CONFIG_SCHEMA = vol.Schema({
    DOMAIN: vol.Schema({
        vol.Required(CONF_URL): cv.url,
        vol.Optional(CONF_PROXY, default=None): cv.url
    })
}, extra=vol.ALLOW_EXTRA)

DATA_CONTROL4 = 'control4'
DATA_CONTROL4_CONFIG = 'control4_config'

REQUIREMENTS = ['python-control4-lite===0.1.15']

async def async_setup(hass, config):
    """Setup Control4 Controller"""

    _LOGGER.debug('async_setup: %s', str(config))

    if DOMAIN not in config:
        return

    _LOGGER.debug('async_setup has config')

    from control4 import Control4

    conf = config[DOMAIN]

    # May need to avoid aviod 'session=async_get_clientsession(hass)' here,
    # because Control4 seems to require force_close -- TBD
    control4 = Control4(url=conf['url'], session=async_get_clientsession(hass), proxy=conf['proxy'])

    hass.data[DATA_CONTROL4_CONFIG] = conf
    hass.data[DATA_CONTROL4] = Control4Device(hass, conf, control4)

    return True

class Control4Device:
    """Structure Control4 functions for hass."""

    def __init__(self, hass, conf, control4):
        """Init Control4 Devices"""
        self._hass = hass
        self._conf = conf
        self._control4 = control4

    @property
    def control4(self):
        return self._control4


#coding=utf-8
import logger
import modeling

from java.util import HashMap
from java.util import ArrayList

from appilog.common.system.types.vectors import ObjectStateHolderVector
from appilog.common.system.types import ObjectStateHolder

import Util
import Queries

INTERNAL_PRCEDURES_PREFIXES = ['sp_', 'xp_', 'ms_', 'sysmail_']

VALUES = ["IsAutoClose","Recovery","IsAutoShrink","cmptlevel","IsAutoUpdateStatistics","IsAutoCreateStatistics","IsAutoClose","Collation"]

class SqlDbProps:

    def __init__(self,connection, discoveryOptions):
        self.connection = connection
        self.discoveryOptions = discoveryOptions

    def getDatabases(self,databases,hostId,discoverConfigs=1):
        dbList = Util.mapToInString(databases,Queries.DATA_NAME)
        configFiles=self.getDatabaseProperties(databases,dbList,discoverConfigs)
        backups = self.getBackup(databases,dbList,hostId)
        oshv = ObjectStateHolderVector()
        itr = databases.values()
        map(oshv.add, itr)
        oshv.addAll(configFiles)
        oshv.addAll(backups)
        return oshv

    def __genAdditionalFilter(self):
        group_prefixes_by_len = {}
        for internal_procedure_prefix in INTERNAL_PRCEDURES_PREFIXES:
            lens = group_prefixes_by_len.setdefault(len(internal_procedure_prefix), [])
            lens.append('\'%s\'' % internal_procedure_prefix)
        result = ""
        for prefix_len in group_prefixes_by_len.keys():
            result += " AND LEFT(ROUTINE_NAME, %d) NOT IN (%s)" % (prefix_len, ", ".join(group_prefixes_by_len.get(prefix_len)))
        logger.debug(result)
        return result

    def getStoredProcedureFromDB(self, dbName, container, filterInternal):
        result = ObjectStateHolderVector()
        if not self.discoveryOptions.discoverProcedures:
            return result
        # if we working with master table need to ignore MSSQL internal storage procedure
        additionalFilter = ""
        if filterInternal:
            additionalFilter = self.__genAdditionalFilter()

        rs = self.connection.getTable('SELECT ROUTINE_NAME, ROUTINE_TYPE, CREATED, LAST_ALTERED FROM [%s].information_schema.routines WHERE routine_type = \'PROCEDURE\'%s' % (dbName, additionalFilter))
        while rs.next():
            name = rs.getString('ROUTINE_NAME')
            dba_type = rs.getString('ROUTINE_TYPE')
            created = rs.getTimestamp('CREATED')
            last_updated = rs.getTimestamp('LAST_ALTERED')

            if name:
                storedProcedure = ObjectStateHolder('dbaobjects')
                storedProcedure.setContainer(container)
                storedProcedure.setAttribute('name', name)
                storedProcedure.setAttribute('dbaobjects_owner', dbName)
                storedProcedure.setAttribute('dbaobjects_type', dba_type)
                if created:
                    storedProcedure.setDateAttribute('dbaobjects_created', created)
                if last_updated:
                    storedProcedure.setDateAttribute('dbaobjects_lastddltime', last_updated)
                result.add(storedProcedure)
        return result

    def getStoredProcedures(self, databases):
        result = ObjectStateHolderVector()
        if not self.discoveryOptions.discoverProcedures or not databases or databases.isEmpty():
            return result
        for dbName in databases.keySet():
            if dbName:
                filterInternal = not self.discoveryOptions.discoverInternalProcedures and (dbName in  ['master', 'msdb'])
                result.addAll(self.getStoredProcedureFromDB(dbName, databases.get(dbName), filterInternal))
        return result

    def getDatabaseProperties(self,databases,dbList,discoverConfigs=1):
        oshv = ObjectStateHolderVector()
        if not discoverConfigs:
            return oshv
        query = Util.replace(Queries.DATABASE_CONFIG_FILE.toString(),dbList)
        rs = self.connection.getTable(query)
        logger.debug("get db properties")
        while rs.next():
            dataBasePropsList=[]
            dbName = rs.getString("name")
            database = databases.get(dbName)
            for key in VALUES:
                value = rs.getString('_'+key)
                if value:
                    dataBasePropsList.append("%s=%s\n" % (key, value))
            data = ''.join(dataBasePropsList)
            configFileOsh = modeling.createConfigurationDocumentOSH('mssql database configuration.txt', 'virtual', data, database, modeling.MIME_TEXT_PLAIN)
            oshv.add(configFileOsh)

        logger.debug("get db properties: ", oshv.toXmlString())
        rs.close()
        return oshv

    def getBackup(self,databases,dbList,hostId):
        oshv = ObjectStateHolderVector()
        backupMap = self.getDBBackups(databases,dbList)
        backupFilesMap=self.getBackupFiles(databases,dbList,hostId,oshv)
        #create the use link
        logger.debug("get backup files")
        for key in backupMap.keySet():
            backup = backupMap.get(key)
            backupFile =backupFilesMap.get(key)
            if backup is not None and backupFile is not None:
                oshv.add(backup)
                oshv.add(backupFile)
                link = modeling.createLinkOSH("use", backup, backupFile)
                oshv.add(link)
            logger.debug("got backup files: ", oshv.size())
        return oshv

    def getBackupFiles(self,databases,dbList,hostId,oshvResult):
        result = HashMap()
        query = Util.replace(Queries.DATABASE_BACKUP_FILES,dbList)
        rs = self.connection.getTable(query)
        disks = ArrayList()
        while rs.next():
            currentDbName = rs.getString("name")
            path = rs.getString("path")
            if path is not None:
                #extract the path
                path = Util.replaceFileSeparator(path)
                disk = Util.getDisk(path,hostId)
                disks.add(disk)

                fileName = Util.getFileFromPath(path)
                path =  Util.getPath(path)
                configFileOsh = modeling.createConfigurationDocumentOSH(fileName, path, None, hostId)
                result.put(currentDbName, configFileOsh)

        itr = disks.iterator()
        while itr.hasNext():
            oshvResult.add(itr.next())
        rs.close()
        return result

    def getDBBackups(self,databases,dbList):
        result = HashMap()
        query = Util.replace(Queries.DATABASE_BACKUP,dbList)
        rs = self.connection.getTable(query)
        while rs.next():
            date = rs.getTimestamp("backupDate")
            if date is not None:
                osh = ObjectStateHolder("sqlbackup")
                dbName = rs.getString("name")
                database = databases.get(dbName)
                osh.setContainer(database)
                osh.setAttribute("sqlbackup_startdate",Util.getSqlDateInGMT(date.getTime()))
                result.put(dbName,osh)
        rs.close()
        return result


import numpy as np
import copy
import tensorflow as tf
from tensorflow.keras.layers import Dense
from collections import deque
import random
import matplotlib.pyplot as plt
import time
import pickle
import scipy.io

from hyper_parameter_3 import hp

class Env:
    def __init__(self, step_size, goal_bound, joint1_range, joint2_range):

        self.step_size = step_size
        self.goal_bound = goal_bound

        self.joint1_range = joint1_range
        self.joint2_range = joint2_range

        self.joint_min = np.array([[self.joint1_range[0], self.joint2_range[0]]])
        self.joint_max = np.array([[self.joint1_range[1], self.joint2_range[1]]])
        self.joint_range = self.joint_max - self.joint_min

        self.step_count = 0
        self.success = False
        self.p_len = 0

        self.location = 0
        self.goal = 0
        self.goal_dir = 0
        self.state = 0

        self.obs = []
        self.obs.append(np.array([[2.5, 2.5]]))
        self.obs.append(np.array([[2.5, -2.5]]))
        self.obs.append(np.array([[-2.5, 2.5]]))
        self.obs.append(np.array([[-2.5, -2.5]]))
        self.obs_r = 1.0

        self.margin = self.goal_bound

    def reset(self):
        self.step_count = 0
        self.success = False
        self.p_len = 0

        self.location = (self.joint_range - 2.0 * self.margin) * np.random.rand(1, 2) + self.joint_min + self.margin
        while self.collision_check_init(self.location):
            self.location = (self.joint_range - 2.0 * self.margin) * np.random.rand(1, 2) + self.joint_min + self.margin

        self.goal = (self.joint_range - 2.0 * self.margin) * np.random.rand(1, 2) + self.joint_min + self.margin
        while (np.linalg.norm(self.location - self.goal) <= 2.0*self.goal_bound) | self.collision_check_init(self.goal):
            self.goal = (self.joint_range - 2.0 * self.margin) * np.random.rand(1, 2) + self.joint_min + self.margin

        self.state = np.concatenate((self.location, self.goal), axis=1)

        return copy.deepcopy(self.state)

    def step(self, action):

        self.step_count += 1

        a_len = np.linalg.norm(action)
        if a_len > 1.0:
            action = action/a_len

        next_location = self.location + self.step_size*action + np.random.randn(1, 2)/500

        if (next_location > self.joint_max).any() | (next_location < self.joint_min).any() | self.collision_check(next_location):
            reward = -1.0
            done = False

            self.p_len += self.step_size
        else:
            goal_d = np.linalg.norm(next_location - self.goal)
            self.location = np.copy(next_location)

            self.p_len += self.step_size * np.linalg.norm(action)

            if goal_d <= self.goal_bound:
                reward = 0.0
                done = True
                self.success = True
            else:
                reward = -1.0
                done = False

        self.state = np.concatenate((self.location, self.goal), axis=1)

        return copy.deepcopy(self.state), reward, done

    def collision_check_init(self, config_p):
        flag = False
        for i in range(len(self.obs)):
            if np.linalg.norm(config_p - self.obs[i]) <= self.obs_r + self.margin:
                flag = True
                break

        return flag

    def collision_check(self, config_p):

        flag = False

        for i in range(len(self.obs)):
            if np.linalg.norm(config_p - self.obs[i]) <= self.obs_r:
                flag = True
                break

        return flag

class DiagonalGaussian():
    def __init__(self, dim):
        self._dim = dim
        self._tiny = 1e-8

    @property
    def dim(self):
        return self._dim

    def likelihood_ratio(self, x, old_param, new_param):
        llh_new = self.log_likelihood(x, new_param)
        llh_old = self.log_likelihood(x, old_param)
        return tf.math.exp(llh_new - llh_old)

    def log_likelihood(self, x, param):
        """
        Compute log likelihood as:
        -N/2log(2*pi*sigma^2)-1/(2*sigma^2) * Sum(x - mu)^2
        """
        means = param["mean"]
        log_stds = param["log_std"]
        assert means.shape == log_stds.shape
        zs = (x - means) / tf.exp(log_stds)
        return - tf.reduce_sum(log_stds, axis=-1) \
               - 0.5 * tf.reduce_sum(tf.square(zs), axis=-1) \
               - 0.5 * self.dim * tf.math.log(2 * np.pi)

    def sample(self, param):
        means = param["mean"]
        log_stds = param["log_std"]
        # reparameterization
        return means + tf.random.normal(shape=means.shape) * tf.math.exp(log_stds)

    def entropy(self, param):
        log_stds = param["log_std"]
        return tf.reduce_sum(log_stds + tf.math.log(tf.math.sqrt(2 * np.pi * np.e)), axis=-1)

class GaussianActor(tf.keras.Model):
    LOG_SIG_CAP_MAX = 2  # np.e**2 = 7.389
    LOG_SIG_CAP_MIN = -20  # np.e**-10 = 4.540e-05
    EPS = 1e-6

    def __init__(self, state_shape, action_dim, max_action, hidden_activation="relu",
                 tanh_mean=False, tanh_std=False,
                 fix_std=False, const_std=0.1,
                 state_independent_std=False, name='GaussianPolicy'):
        super().__init__(name=name)
        self.dist = DiagonalGaussian(dim=action_dim)
        self._fix_std = fix_std
        self._tanh_std = tanh_std
        self._const_std = const_std
        self._max_action = max_action
        self._state_independent_std = state_independent_std

        self.l1 = Dense(400, name="L1", activation=hidden_activation)
        self.l2 = Dense(300, name="L2", activation=hidden_activation)
        self.out_mean = Dense(action_dim, name="L_mean", activation='tanh')
        activation = 'tanh' if tanh_std else None
        self.out_log_std = Dense(action_dim, name="L_sigma", activation=activation)

    def _compute_dist(self, states):
        features = self.l1(states)
        features = self.l2(features)
        mean = self.out_mean(features)
        log_std = self.out_log_std(features)
        log_std = tf.clip_by_value(log_std, self.LOG_SIG_CAP_MIN, self.LOG_SIG_CAP_MAX)

        return {"mean": mean, "log_std": log_std}

    def call(self, states, test=False):
        param = self._compute_dist(states)
        if test:
            raw_actions = param["mean"]
        else:
            raw_actions = self.dist.sample(param)
        logp_pis = self.dist.log_likelihood(raw_actions, param)

        actions = raw_actions

        return actions * self._max_action, logp_pis, param

class CriticV(tf.keras.Model):
    def __init__(self, state_shape, name='vf'):
        super().__init__(name=name)

        self.l1 = Dense(400, name="L1", activation='relu')
        self.l2 = Dense(300, name="L2", activation='relu')
        self.l3 = Dense(1, name="L3", activation='linear')

    def call(self, states):
        features = self.l1(states)
        features = self.l2(features)
        values = self.l3(features)

        return tf.squeeze(values, axis=1, name="values")

class CriticQ(tf.keras.Model):
    def __init__(self, state_shape, action_dim, name='qf'):
        super().__init__(name=name)

        self.l1 = Dense(400, name="L1", activation='relu')
        self.l2 = Dense(300, name="L2", activation='relu')
        self.l3 = Dense(1, name="L2", activation='linear')

    def call(self, inputs):
        [states, actions] = inputs
        features = tf.concat([states, actions], axis=1)
        features = self.l1(features)
        features = self.l2(features)
        values = self.l3(features)

        return tf.squeeze(values, axis=1)

class SAC():
    def __init__(self):
        super().__init__()

        gpu = 0
        max_action = 1.
        state_shape = hp.state_dim #env.observation_space.shape
        action_dim = hp.action_dim #env.action_space.high.size

        lr = 3e-4
        sigma = 0.1
        tau = 0.005
        self.state_ndim = len((1, state_shape))

        self.policy_name = "SAC"
        self.update_interval = 1
        self.batch_size = 512
        self.discount = 0.98
        self.n_warmup = 2000
        self.n_epoch = 1
        self.max_grad = 10
        self.memory_capacity = 1000000
        self.device = "/gpu:{}".format(gpu) if gpu >= 0 else "/cpu:0"

        # Define and initialize Actor network
        self.actor = GaussianActor(state_shape, action_dim, max_action, tanh_mean=False, tanh_std=False)
        self.actor_optimizer = tf.keras.optimizers.Adam(learning_rate=lr)

        # Define and initialize Critic network
        self.qf1 = CriticQ(state_shape, action_dim, name="qf1")
        self.qf2 = CriticQ(state_shape, action_dim, name="qf2")
        self.qf1_optimizer = tf.keras.optimizers.Adam(learning_rate=lr)
        self.qf2_optimizer = tf.keras.optimizers.Adam(learning_rate=lr)

        self.vf = CriticV(state_shape)
        self.vf_target = CriticV(state_shape)
        self.vf_optimizer = tf.keras.optimizers.Adam(learning_rate=lr)

        # Set hyper-parameters
        self.sigma = sigma
        self.tau = tau

        self.log_alpha = tf.Variable(0., dtype=tf.float32)
        self.alpha = tf.Variable(0., dtype=tf.float32)
        self.alpha.assign(tf.exp(self.log_alpha))
        self.target_alpha = -action_dim
        self.alpha_optimizer = tf.keras.optimizers.Adam(learning_rate=lr)

    def _save_model(self):
        self.actor.save_weights('./save/actor', save_format='tf')
        self.qf1.save_weights('./save/qf1', save_format='tf')
        self.qf2.save_weights('./save/qf2', save_format='tf')
        self.vf.save_weights('./save/vf', save_format='tf')
        self.vf_target.save_weights('./save/vf_target', save_format='tf')

    def _load_model(self):
        self.actor.load_weights('./save/actor')
        self.qf1.load_weights('./save/critic')
        self.qf2.load_weights('./save/critic_target')
        self.vf.load_weights('./save/critic_2')
        self.vf_target.load_weights('./save/critic_2_target')

    def get_action(self, state, test=False):
        assert isinstance(state, np.ndarray)
        is_single_state = len(state.shape) == self.state_ndim

        state = np.expand_dims(state, axis=0).astype(
            np.float32) if is_single_state else state
        action = self._get_action_body(tf.constant(state), test)

        return action.numpy()[0] if is_single_state else action

    @tf.function
    def _get_action_body(self, state, test):
        return self.actor(state, test)[0]

if __name__ == "__main__":

    joint1_range = [-5.0, 5.0]
    joint2_range = [-5.0, 5.0]

    step_size = 0.5
    goal_bound = 0.1

    env = Env(step_size=step_size, goal_bound=goal_bound, joint1_range=joint1_range, joint2_range=joint2_range)

    state_dim = 4
    action_dim = 2

    agent = SAC()

    agent._load_model()

    global_step = 0
    scores = []
    suc_rate = []
    p_len = []

    print(step_size, goal_bound)
    for e in range(20):

        state = env.reset()
        #state = np.reshape(state, [1, state_dim])

        done = False

        local_step = 0
        score = 0

        visual_memory = []

        while (done is False) & (env.step_count < 100):
            global_step += 1
            local_step += 1

            visual_memory.append(env.location)

            action = agent.get_action(state, test=True)

            next_state, reward, done = env.step(action)

            state = next_state
            score += reward

        visual_memory.append(env.location)

        suc_rate.append(env.success)
        p_len.append(env.p_len)
        scores.append(score)

        print('local_step:', local_step, '/ score:', score)

        plt.figure(figsize=(6,6))
        path = np.reshape(visual_memory, [local_step+1, 2])
        circle1=plt.Circle(env.obs[0][0],env.obs_r,color='k')
        circle2=plt.Circle(env.obs[1][0],env.obs_r,color='k')
        circle3=plt.Circle(env.obs[2][0],env.obs_r,color='k')
        circle4=plt.Circle(env.obs[3][0],env.obs_r,color='k')
        plt.gca().add_artist(circle1)
        plt.gca().add_artist(circle2)
        plt.gca().add_artist(circle3)
        plt.gca().add_artist(circle4)
        plt.plot(path[0, 0], path[0, 1], 'kD', markersize=8, mfc='none', markeredgewidth=2., label='start')
        plt.plot(env.goal[0, 0], env.goal[0, 1], 'ko', markersize=10, mfc='none', markeredgewidth=2., label='goal')
        plt.plot(path[:,0],path[:,1],'b')
        plt.plot(path[:,0],path[:,1],'b.')
        plt.xlim(-5,5)
        plt.ylim(-5,5)
        plt.show()


import torch
import math
import numpy as np
import torch.nn as nn
from torch.autograd import Variable
# from SolarForecasting.ModulesLearning.ModuleLSTM.functions import *
from torch.nn.modules.activation import MultiheadAttention

class EarlyStopping:
    """Early stops the training if validation loss doesn't improve after a given patience."""
    def __init__(self, saving_path, patience=7, verbose=False, delta=0):
        """
        Args:
            patience (int): How long to wait after last time validation loss improved.
                            Default: 7
            verbose (bool): If True, prints a message for each validation loss improvement.
                            Default: False
            delta (float): Minimum change in the monitored quantity to qualify as an improvement.
                            Default: 0
        """
        self.patience = patience
        self.verbose = verbose
        self.counter = 0
        self.best_score = None
        self.early_stop = False
        self.val_loss_min = np.Inf
        self.delta = delta
        self.saving_path = saving_path

    def __call__(self, val_loss, model, epoch, parallel=False):

        score = -val_loss

        if self.best_score is None:
            self.best_score = score
            self.save_checkpoint(val_loss, model,parallel)
        elif (score < self.best_score + self.delta):
            self.counter += 1
            print('EarlyStopping counter: {} out of {}'.format(self.counter, self.patience))
            if self.counter >= self.patience:
                self.early_stop = True
        elif epoch>20:
            self.best_score = score
            self.save_checkpoint(val_loss, model, parallel)
            self.counter = 0

    def save_checkpoint(self, val_loss, model,parallel):
        '''Saves model when validation loss decrease.'''
        if self.verbose:
            print('Validation loss decreased ({} --> {}).  Saving model ...'.format(self.val_loss_min, val_loss))
        if parallel==True:
            torch.save(model.module.state_dict(), self.saving_path)
        else:
            torch.save(model.state_dict(), self.saving_path)
        self.val_loss_min = val_loss

class quantileLSTM(nn.Module):

    # def __init__(self, num_classes, input_size, hidden_size, num_layers)
    def __init__(self, input_dim, timesteps, folder_saving, model, quantile, hidden_size=25, alphas=None, outputs=None, valid=False, dropout = 0.3, num_layers=1):
        super(quantileLSTM, self).__init__()
        self.quantile = quantile
        if self.quantile:
            assert outputs == len(alphas), "The outputs and the quantiles should be of the same dimension"
        else:
            outputs = 1

        self.input_dim = input_dim
        self.hidden_size = hidden_size
        self.num_layers = num_layers
        self.timesteps = timesteps
        self.alphas = alphas
        self.outputs = outputs
        self.valid = valid
        self.train_mode = False
        self.saving_path = folder_saving + model

        self.lstm = nn.LSTM(input_size=self.input_dim, hidden_size=self.hidden_size,
                            num_layers=self.num_layers, batch_first=True, dropout = dropout)
        self.relu = nn.ReLU()

        self.fc = nn.Linear(self.hidden_size, self.outputs)

    def forward(self, x):
        h_0 = Variable(torch.zeros(
            self.num_layers, x.size(0), self.hidden_size,device=x.device))

        c_0 = Variable(torch.zeros(
            self.num_layers, x.size(0), self.hidden_size,device=x.device))

        # Propagate input through LSTM
        ula, (h_out, _) = self.lstm(x, (h_0, c_0))

        h_out = h_out.view(-1, self.hidden_size)

        out = self.fc(self.relu(h_out))

        return out

#
# class SimplePositionalEncoding(torch.nn.Module):
#     def __init__(self, d_model, dropout=0.1, max_len=5000):
#         super(SimplePositionalEncoding, self).__init__()
#         self.dropout = torch.nn.Dropout(p=dropout)
#         pe = torch.zeros(max_len, d_model)
#         position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
#         div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
#         pe[:, 0::2] = torch.sin(position * div_term)
#         pe[:, 1::2] = torch.cos(position * div_term)
#         pe = pe.unsqueeze(0).transpose(0, 1)
#         self.register_buffer('pe', pe)
#
#     def forward(self, x: torch.Tensor) -> torch.Tensor:
#         """Creates a basic positional encoding"""
#         x = x + self.pe[:x.size(0), :]
#         return self.dropout(x)

# class EncoderLayer(torch.nn.Module):
#     def __init__(self, dim_val, dim_attn, n_heads=1):
#         super(EncoderLayer, self).__init__()
#         self.attn = MultiHeadAttentionBlock(dim_val, dim_attn, n_heads)
#         self.fc1 = nn.Linear(dim_val, dim_val)
#         self.fc2 = nn.Linear(dim_val, dim_val)
#
#         self.norm1 = nn.LayerNorm(dim_val)
#         self.norm2 = nn.LayerNorm(dim_val)
#
#     def forward(self, x):
#         a = self.attn(x)
#         x = self.norm1(x + a)
#
#         a = self.fc1(F.elu(self.fc2(x)))
#         x = self.norm2(x + a)
#
#         return x
#
#
# class DecoderLayer(torch.nn.Module):
#     def __init__(self, dim_val, dim_attn, n_heads=1):
#         super(DecoderLayer, self).__init__()
#         self.attn1 = MultiHeadAttentionBlock(dim_val, dim_attn, n_heads)
#         self.attn2 = MultiHeadAttentionBlock(dim_val, dim_attn, n_heads)
#         self.fc1 = nn.Linear(dim_val, dim_val)
#         self.fc2 = nn.Linear(dim_val, dim_val)
#
#         self.norm1 = nn.LayerNorm(dim_val)
#         self.norm2 = nn.LayerNorm(dim_val)
#         self.norm3 = nn.LayerNorm(dim_val)
#
#     def forward(self, x, enc):
#         a = self.attn1(x)
#         x = self.norm1(a + x)
#
#         a = self.attn2(x, kv=enc)
#         x = self.norm2(a + x)
#
#         a = self.fc1(F.elu(self.fc2(x)))
#
#         x = self.norm3(x + a)
#         return x
#
#
#
# class Transformer(nn.Module):
#
#     def __init__(self, input_dim, timesteps, folder_saving, model, quantile, alphas = None, outputs = None, valid = False, out_seq_len=1, n_decoder_layers=3, n_encoder_layers=3,
#                  n_heads=1):
#
#         self.outputs = outputs
#         self.n_decoder_layers = n_decoder_layers
#         self.n_encoder_layers = n_encoder_layers
#         self.input_dim = input_dim
#         self.timesteps = timesteps
#         self.alphas = alphas
#         self.valid = valid
#         self.train_mode = False
#         self.saving_path = folder_saving + model
#         self.quantile = quantile
#         self.dec_seq_len = timesteps
#         self.out_seq_len = out_seq_len
#
#         super(Transformer, self).__init__()
#         # Initiate encoder and Decoder layers
#         self.encs = []
#         for i in range(n_encoder_layers):
#             self.encs.append(EncoderLayer(self.input_dim, self.input_dim//4, n_heads))
#
#         self.decs = []
#         for i in range(n_decoder_layers):
#             self.decs.append(DecoderLayer(self.input_dim, self.input_dim//4, n_heads))
#
#         self.pos = PositionalEncoding(self.input_dim)
#
#         # Dense layers for managing network inputs and outputs
#         # self.enc_input_fc = nn.Linear(input_size, self.input_dim)
#         # self.dec_input_fc = nn.Linear(input_size, self.input_dim)
#         self.out_fc = nn.Linear(self.dec_seq_len * self.input_dim,self.out_seq_len)
#
#     def forward(self, x):
#         # encoder
#         # e = self.encs[0](self.pos(self.enc_input_fc(x)))
#         x = x.transpose(1,2)
#         e = self.encs[0](self.pos(x))
#         for enc in self.encs[1:]:
#             e = enc(e)
#
#         # decoder
#         # d = self.decs[0](self.dec_input_fc(x[:, -self.dec_seq_len:]), e)
#         d = self.decs[0](x[:, -self.dec_seq_len:], e)
#         for dec in self.decs[1:]:
#             d = dec(d, e)
#
#         # output
#         x = self.out_fc(d.flatten(start_dim=1))
#
#         return x

class PositionalEncoding(nn.Module):

    def __init__(self, d_model, max_len=5000):
        super(PositionalEncoding, self).__init__()
        pe = torch.zeros(max_len, d_model)
        position = torch.arange(0, max_len, dtype=torch.float).unsqueeze(1)
        div_term = torch.exp(torch.arange(0, d_model, 2).float() * (-math.log(10000.0) / d_model))
        pe[:, 0::2] = torch.sin(position * div_term)
        pe[:, 1::2] = torch.cos(position * div_term)
        pe = pe.unsqueeze(0).transpose(0, 1)
        #pe.requires_grad = False
        self.register_buffer('pe', pe)

    def forward(self, x):
        return x + self.pe[:x.size(0), :]

class TransAm(nn.Module):
    def __init__(self, input_dim, timesteps, folder_saving, model, quantile, alphas = None, outputs = None, valid = False,num_heads=4, d_model=128, num_layers=2, dropout=0.1):
        super(TransAm, self).__init__()
        self.model_type = 'Transformer'

        self.quantile = quantile
        if self.quantile:
            assert outputs == len(alphas), "The outputs and the quantiles should be of the same dimension"
        else:
            outputs = 1

        self.outputs = outputs
        self.num_layers = num_layers
        self.input_dim = input_dim
        self.d_model = d_model
        self.num_heads = num_heads
        self.timesteps = timesteps
        self.alphas = alphas
        self.valid = valid
        self.train_mode = False
        self.saving_path = folder_saving + model

        self.src_mask = None

        self.input_embedding = nn.Conv1d(self.input_dim, self.d_model, 1)
        self.pos_encoder = PositionalEncoding(self.d_model)

        # self.encoder_layer = nn.TransformerEncoderLayer(d_model=self.input_dim, nhead=2, dim_feedforward=200,dropout=dropout) #embed_dim must be divisible by n_heads

        self.encoder_layer = nn.TransformerEncoderLayer(self.d_model, nhead=self.num_heads, dim_feedforward=200, dropout=dropout) #embed_dim must be divisible by n_heads

        self.transformer_encoder = nn.TransformerEncoder(self.encoder_layer, num_layers=self.num_layers)
        self.decoder = nn.Linear(self.d_model, self.outputs)
        self.init_weights()

    def init_weights(self):
        initrange = 0.1
        self.decoder.bias.data.zero_()
        self.decoder.weight.data.uniform_(-initrange, initrange)

    def forward(self, src):
        src = src.transpose(1, 2)
        src = self.input_embedding(src)  #batch,d_model,timesteps
        src = src.transpose(1,2)  #batch,timesteps,d_model
        src = src.transpose(0,1) #timesteps,batch,d_model

        if self.src_mask is None or self.src_mask.size(0) != len(src):
            device = src.device
            mask = self._generate_square_subsequent_mask(len(src)).to(device)
            self.src_mask = mask

        src = self.pos_encoder(src)
        output = self.transformer_encoder(src, self.src_mask)  # , self.src_mask)
        output = self.decoder(output)
        return output[-1]

    def _generate_square_subsequent_mask(self, sz):
        mask = (torch.triu(torch.ones(sz, sz)) == 1).transpose(0, 1)
        mask = mask.float().masked_fill(mask == 0, float('-inf')).masked_fill(mask == 1, float(0.0))
        return mask

# class MultiAttnHeadSimple(torch.nn.Module):
#     """A simple multi-head attention model inspired by Vaswani et al."""
#
#     def __init__(
#             self, input_dim, seq_len, folder_saving, model, quantile, n_layers=2, factor=12, alphas=None, outputs=None, valid=False,
#
#          output_seq_len=1, num_heads=4, d_model=96, dropout=0.2): #0.5):
#
#         super(MultiAttnHeadSimple, self).__init__()
#
#         self.outputs = outputs
#         self.input_dim = input_dim
#         self.seq_len = seq_len
#         self.alphas = alphas
#         self.valid = valid
#         self.train_mode = False
#         self.saving_path = folder_saving + model
#         self.quantile = quantile
#         self.num_heads = num_heads
#         self.n_layers = n_layers
#
#         self.output_seq_len = output_seq_len
#         self.factor = factor
#         self.d_model = d_model
#         self.dropout = dropout
#
#         # #If solving multi horizon problem
#         # if output_seq_len>1:
#         #     self.factor = self.output_seq_len
#         #
#         #
#         # #self.factor = self.seq_len #setting this when not using dense interpolation
#         #
#         self.encoder = EncoderLayer(self.input_dim, self.seq_len, self.num_heads, self.n_layers, self.d_model, self.dropout)
#         #self.dense_interpolation = DenseInterpolation(self.seq_len, self.factor)
#         #
#         # if self.output_seq_len>1:
#         #     self.fc = nn.Linear(self.d_model, self.outputs)
#         # else:
#         self.fc = nn.Linear(int(self.d_model * self.seq_len), self.outputs)
#
#         # if self.output_seq_len >1:
#             # for i in range(self.output_seq_len):
#             #     setattr(self, "dense%d" % i, DenseInterpolation(self.seq_len, self.factor))
#             #     setattr(self, "fc%d" % i, nn.Linear(int(self.d_model * self.factor), self.outputs))
#         #
#         #
#
#
#
#     #     # self.dense_shape = torch.nn.Linear(number_time_series, d_model)
#     #     self.pe = SimplePositionalEncoding(self.d_model)
#     #     self.multi_attn = MultiheadAttention(
#     #         embed_dim=self.d_model, num_heads=self.num_heads, dropout=dropout)
#     #     self.final_layer = torch.nn.Linear(self.d_model, self.outputs)
#     #     self.length_data = seq_len
#     #     self.forecast_length = output_seq_len
#     #     self.last_layer = torch.nn.Linear(seq_len, output_seq_len)
#     #
#     #
#     # def forward(self, x: torch.Tensor, mask=None) -> torch.Tensor:
#     #
#     #     # Permute to (L, B, M)
#     #     x = x.permute(2, 0, 1)
#     #     # x = self.dense_shape(x)
#     #     x = self.pe(x)
#     #     if mask is None:
#     #         x = self.multi_attn(x, x, x)[0]
#     #     else:
#     #         x = self.multi_attn(x, x, x, attn_mask=self.mask)[0]
#     #     x = self.final_layer(x)
#     #
#     #     # Switch to (B, M, L)
#     #     x = x.permute(1, 2, 0)
#     #     x = self.last_layer(x)
#     #
#     #     if self.forecast_length>1:
#     #         return x
#     #     else:
#     #         return x[:,:,-1]
#
#
#     def forward(self, x: torch.Tensor) -> torch.Tensor:
#         # print("input x shape", x.shape)
#         x = self.encoder(x)
#         # print("after encoding", x.shape)
#         #x = self.dense_interpolation(x)
#         # x = x.transpose(1,2)
#         if self.output_seq_len==1:
#             x = x.contiguous().view(-1, int(self.seq_len * self.d_model))
#         x = self.fc(x)
#         # x = x.contiguous().view(-1, int(self.factor * self.d_model))
#         pred_outputs = {}
#
#         # for i in range(self.output_seq_len):
#         #     y = getattr(self, "dense%d" % i)(x)
#         #     y = y.contiguous().view(-1, int(self.factor * self.d_model))
#         #     pred_outputs[i] = getattr(self, "fc%d" % i)(y)
#
#         # print("final output", x.shape)
#         return x
#         # return pred_outputs

def trainBatchwise(trainX, trainY, epochs, batch_size, lr, validX,
                  validY, n_output_length, n_features, n_timesteps, folder_saving, model_saved, quantile, n_layers, alphas, outputs, valid, output_seq_len, num_heads, d_model, patience=None, verbose=None, reg_lamdba = 0):

#def trainBatchwise(trainX, trainY, epochs, batch_size, lr, validX,
 #                  validY, n_output_length, n_features, n_timesteps, folder_saving, model_saved, quantile,hidden_size, alphas, outputs, valid, patience=None, verbose=None,
  #                 reg_lamdba=0):
    train_on_gpu = torch.cuda.is_available()
    print(train_on_gpu)
    # device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")

    parallel = False
 #   quantile_forecaster = quantileLSTM(n_features, n_timesteps, folder_saving, model_saved, quantile, hidden_size, alphas = alphas, outputs = outputs, valid=valid)

    #quantile_forecaster = MultiAttnHeadSimple(n_features, n_timesteps, folder_saving, model_saved, quantile, n_layers, factor, alphas = alphas, outputs = outputs, valid=valid, output_seq_len = output_seq_len, num_heads=num_heads, d_model=d_model)
    quantile_forecaster = TransAm(n_features, n_timesteps, folder_saving, model_saved, quantile, alphas=alphas,
                                  outputs=outputs, valid=valid, num_heads=num_heads, d_model=d_model,
                                  num_layers=n_layers)

    if train_on_gpu:
        # if torch.cuda.device_count() > 1:
        #     print("Let's use", torch.cuda.device_count(), "GPUs!")
        #     quantile_forecaster = nn.DataParallel(quantile_forecaster)
        #     parallel=True
        quantile_forecaster = quantile_forecaster.cuda()

        # point_foreaster = point_foreaster.cuda()

    print(quantile_forecaster)

    optimizer = torch.optim.Adam(quantile_forecaster.parameters(), lr=lr) #,betas=(0.9, 0.999), eps=1e-08, weight_decay = 1e-5) #, betas = (0.9,0.98)) # clipnorm is here for LSTM
    # scheduler = StepLR(optimizer, step_size=25, gamma=0.1)
    criterion = torch.nn.MSELoss()
    # criterion = nn.L1Loss()
    samples = trainX.size()[0]
    losses = []
    valid_losses = []

    saving_path = folder_saving + model_saved
    early_stopping = EarlyStopping(saving_path, patience=patience, verbose=True)
    train_mode = False

    for epoch in range(epochs):
        if train_mode is not True:
            quantile_forecaster.train()
            train_mode = True

        indices = torch.randperm(samples)
        trainX, trainY = trainX[indices, :, :], trainY[indices]
        per_epoch_loss = 0
        count_train = 0
        for i in range(0, samples, batch_size):
            xx = trainX[i: i + batch_size, :, :]
            yy = trainY[i: i + batch_size]

            if train_on_gpu:
                xx, yy = xx.cuda(), yy.cuda()

            outputs = quantile_forecaster.forward(xx)
            optimizer.zero_grad()
            if quantile:
                if n_output_length == 1:
                    loss = quantile_loss(outputs, yy, alphas)
                else:
                    # train loss for multiple outputs or multi-task learning
                    total_loss = []
                    for n in range(n_output_length):
                        # y_pred = outputs[:,n, :]
                        y_pred = outputs[n]
                        # calculate the batch loss
                        loss = quantile_loss(y_pred, yy[:, n], alphas)
                        total_loss.append(loss)

                    loss = sum(total_loss)

            else:
                loss = criterion(outputs, yy)

            reg_loss = np.sum([weights.norm(2) for weights in quantile_forecaster.parameters()])
            total_loss = loss + reg_lamdba / 2 * reg_loss
            # backward pass: compute gradient of the loss with respect to model parameters
            total_loss.backward()
    #        torch.nn.utils.clip_grad_norm_(quantile_forecaster.parameters(), 1)
            # perform a single optimization step (parameter update)
            optimizer.step()
            # scheduler.step()
            per_epoch_loss += loss.item()
            count_train += 1

        train_loss_this_epoch = per_epoch_loss / count_train
        losses.append(train_loss_this_epoch)

        if valid:
            train_mode = False
            quantile_forecaster.eval()
            if train_on_gpu:
                validX, validY = validX.cuda(), validY.cuda()

            if quantile:
                validYPred = quantile_forecaster.forward(validX)
                # validYPred = validYPred.cpu().detach().numpy()
                # validYTrue = validY.cpu().detach().numpy()
                # valid_loss_this_epoch = self.quantile_loss(validYPred,validY).item()

                if n_output_length == 1:
                    valid_loss_this_epoch = quantile_loss(validYPred, validY, alphas).item()
                else:
                    # train loss for multiple outputs or multi-task learning
                    total_loss = []
                    for n in range(n_output_length):
                        # y_pred = validYPred[:, n, :]
                        y_pred = validYPred[n]
                        # calculate the batch loss
                        loss = quantile_loss(y_pred, validY[:, n], alphas)
                        total_loss.append(loss)

                    valid_loss_this_epoch = sum(total_loss).item()

                # valid_loss = elf.crps_score(validYPred, validYTrue, np.arange(0.05, 1.0, 0.05))s
                valid_losses.append(valid_loss_this_epoch)
                print("Epoch: %d, loss: %1.5f and valid_loss : %1.5f" % (epoch, train_loss_this_epoch, valid_loss_this_epoch))
            else:
                validYPred = quantile_forecaster.forward(validX)

                # # valid loss for multiple outputs or multi-task learning
                # total_loss = []
                # for n in range(self.outputs):
                #     y_pred = validYPred[:, n]
                #     # calculate the batch lossnb6h
                #     validloss = criterion(y_pred, validY[:, n])
                #     total_loss.append(validloss)

                # validloss = sum(total_loss)
                # valid_loss_this_epoch = validloss.item()
                valid_loss_this_epoch = criterion(validYPred, validY).item()
                # validYPred = validYPred.cpu().detach().numpy()
                # validYTrue = validY.cpu().detach().numpy()
                # valid_loss = np.sqrt(mean_squared_error(validYPred, validYTrue))
                valid_losses.append(valid_loss_this_epoch)
                print("Epoch: %d, train loss: %1.5f and valid loss : %1.5f" % (
                epoch, train_loss_this_epoch, valid_loss_this_epoch))

            # early_stopping(valid_loss, self)
            early_stopping(valid_loss_this_epoch, quantile_forecaster, epoch, parallel)

            if early_stopping.early_stop:
                print("Early stopping")
                break
        else:
            print("Epoch: %d, loss: %1.5f" % (epoch, train_loss_this_epoch))
            if parallel == True:
                torch.save(quantile_forecaster.module.state_dict(), saving_path)
            else:
                torch.save(quantile_forecaster.state_dict(), saving_path)
        # load the last checkpoint with the best model
    # self.load_state_dict(torch.load('checkpoint.pt'))
    return losses, valid_losses

def crps_score(outputs, target, alphas):
    loss = []
    for i, alpha in enumerate(alphas):
        output = outputs[:, i].reshape((-1, 1))
        covered_flag = (output <= target).astype(np.float32)
        uncovered_flag = (output > target).astype(np.float32)
        if i == 0:
            loss.append(np.mean(
                ((target - output) * alpha * covered_flag + (output - target) * (1 - alpha) * uncovered_flag)))
        else:
            loss.append(np.mean(
                ((target - output) * alpha * covered_flag + (output - target) * (1 - alpha) * uncovered_flag)))

    return 2*np.mean(np.array(loss))

def quantile_loss(outputs, target, alphas):

    for i, alpha in zip(range(len(alphas)),alphas):
        output = outputs[:, i].reshape((-1, 1))
        covered_flag = (output <= target).float()
        uncovered_flag = (output > target).float()
        if i == 0:
            loss = ((target - output) * alpha * covered_flag + (output - target) * (1 - alpha) * uncovered_flag)
        else:
            loss += ((target - output) * alpha * covered_flag + (output - target) * (1 - alpha) * uncovered_flag)

    return torch.mean(loss)


"""Module containing the tests of the Fog object."""

import pytest

from src.fog import Fog

polygons = [
    [(-200, 100), (50, 100), (50, 200), (-200, 200)],
    [(-100, 50), (25, 50), (25, 100), (-100, 100)],
    [(-50, 25), (12.5, 25), (12.5, 50), (-50, 50)],
    [(200, 200), (250, 250), (300, 200)],
    [(180, 180), (225, 225), (270, 180)],
    [(162, 162), (202.5, 202.5), (243, 162)],
    [(145.8, 145.8), (182.25, 182.25), (218.7, 145.8)],
]

centroids = [
    (-75, 150),
    (-37.5, 75),
    (-18.75, 37.5),
    (250, 216.67),
    (225, 195),
    (202.5, 175.5),
    (182.25, 157.95),
]

polygons_order = [
    [0, 1, 2],
    [1, 2, 0],
    [2, 0, 1],
    [0, 1, 2],
    [3, 4, 5, 6],
    [4, 5, 6, 3],
    [5, 6, 3, 4],
    [6, 3, 4, 5],
]

@pytest.fixture(params=polygons_order)
def param_polygon(request):
    """Fixture for polygon centroid tests."""
    return (
        polygons,
        centroids,
        request.param,
    )

def test_get_polygon_centroid(param_polygon):
    """Test the fog method to compute the centroid of a polygon."""
    fog = Fog()
    polygon_order = param_polygon[2]

    for polygon_pos in polygon_order:
        polygon = param_polygon[0][polygon_pos]
        expected_centroid = param_polygon[1][polygon_pos]

        # Compute centroid and check that is as expected (within 1 pixel tolerance)
        computed_centroid = fog.get_polygon_centroid(polygon)
        assert (
            expected_centroid[0] - 1 < computed_centroid[0] < expected_centroid[0] + 1
        )
        assert (
            expected_centroid[1] - 1 < computed_centroid[1] < expected_centroid[1] + 1
        )

def test_center_polygons(param_polygon):
    """Test the fog method to center polygons with respect to a reference polygon."""
    fog = Fog()

    # Get the first polygon as reference and center the others
    # All polygons should have now the same centroid than the first one
    polygon_ref = param_polygon[0][0]
    polygons_list = param_polygon[0][1:]
    centered_polygons = fog.center_polygons(polygon_ref, polygons_list)

    for polygon in centered_polygons:
        # Check that the computed value is in expected range (+- 1 because of the pixel resolution)
        centroid_ref = fog.get_polygon_centroid(polygon_ref)
        centroid = fog.get_polygon_centroid(polygon)
        assert centroid[0] - 1 < centroid_ref[0] < centroid[0] + 1
        assert centroid[1] - 1 < centroid_ref[1] < centroid[1] + 1

def test_get_scaled_polygons():
    """Test fog method to get scaled polygons."""
    fog = Fog()
    i_ref_polygon = 0
    ref_polygon = polygons[i_ref_polygon]
    scale_factor = 0.5
    num_polygons = 3
    scaled_polygons = fog.get_scaled_polygons(ref_polygon, scale_factor, num_polygons)
    assert len(scaled_polygons) == num_polygons
    num_vertices = len(polygons[i_ref_polygon])
    for i_polygon in range(num_polygons - 1):
        ref_polygon = polygons[i_ref_polygon + i_polygon + 1]
        scaled_polygon = scaled_polygons[i_polygon + 1]
        for i_vertex in range(num_vertices):
            for i_coord in range(2):
                assert (
                    scaled_polygon[i_vertex][i_coord] - 1
                    < ref_polygon[i_vertex][i_coord]
                    < scaled_polygon[i_vertex][i_coord] + 1
                )

    i_ref_polygon = 3
    polygon = polygons[i_ref_polygon]
    scale_factor = 0.9
    num_polygons = 4
    scaled_polygons = fog.get_scaled_polygons(polygon, scale_factor, num_polygons)
    assert len(scaled_polygons) == num_polygons
    num_vertices = len(polygons[i_ref_polygon])
    for i_polygon in range(num_polygons - 1):
        ref_polygon = polygons[i_ref_polygon + i_polygon + 1]
        scaled_polygon = scaled_polygons[i_polygon + 1]
        for i_vertex in range(num_vertices):
            for i_coord in range(2):
                assert (
                    scaled_polygon[i_vertex][i_coord] - 1
                    < ref_polygon[i_vertex][i_coord]
                    < scaled_polygon[i_vertex][i_coord] + 1
                )


from flask import Blueprint, request, jsonify, redirect, Response
from models import db, Match, Tournament, User, Game
import random
import auth

game_blueprint = Blueprint('game', __name__)

@game_blueprint.route('/games')
def get_games():
    max_per_page = 100
    page = request.args.get('page', 1, type=int)
    per_page = request.args.get('perPage', 50, type=int)
    q = db.session.query(Game)
    search_term = request.args.get('searchTerm', None, str)
    if search_term:
        q = q.filter((Game.name.ilike('%{}%'.format(search_term))))  # Filter by term
    order_by_str = request.args.get('orderBy', '', str)
    if order_by_str:
        order_by_values = order_by_str.split(',')
    else:
        order_by_values = []
    if order_by_values:
        # Order result by specified and admitted values (multiple order values)
        for order_by_value in order_by_values:
            splitted_order_by_value = order_by_value.split(':')
            value = splitted_order_by_value[0]
            if len(splitted_order_by_value) == 2 and (
                    splitted_order_by_value[1] == 'asc' or splitted_order_by_value[1] == 'desc'):
                direction = splitted_order_by_value[1]
            else:
                direction = 'asc'
            if order_by_value == 'name':
                q = q.order_by()
    else:
        print('ads')
        q = q.order_by(Game.name.asc())
    pagination = q.paginate(page, per_page, max_per_page)  # Paginate result
    return_games = []
    for game in pagination.items:
        return_games.append(game.short())
    return_data = {
        'games': return_games,
        'total_games': pagination.total,
        'page': pagination.page,
        'pages': pagination.pages,
    }
    return jsonify(return_data)

@auth.requires_auth('create:game')
@game_blueprint.route('/games', methods=['POST'])
def create_game():
    data = request.json
    # TODO: check for name field existence + string not empty
    game = db.session.query(Game).filter(Game.name.ilike(data['name'].strip())).first()
    if game:
        resp = jsonify(game.long())
        resp.status_code = 303
        resp.headers['Location'] = '/games/{}'.format(game.id)
        return resp
    game = Game(name=data['name'])
    game.insert()
    return jsonify(game.long()), 201

@game_blueprint.route('/games/<int:game_id>')
def get_game(game_id):
    game = db.session.query(Game).filter(Game.id == game_id).first()
    if not game:
        return {'error': 'Game not found'}, 404
    return jsonify(game.long())

@auth.requires_auth('update:game')
@game_blueprint.route('/games/<int:game_id>', methods=['PATCH'])
def patch_game(game_id):
    game = db.session.query(Game).filter(Game.id == game_id).first()
    if not game:
        return {'error': 'Game not found'}, 404
    data = request.json
    if 'name' in data:
        game.name = data['name']
    db.session.commit()
    return jsonify(game.long())

@auth.requires_auth('delete:game')
@game_blueprint.route('/games/<int:game_id>', methods=['DELETE'])
def delete_game(game_id):
    game = db.session.query(Game).filter(Game.id == game_id).first()
    if not game:
        return {'error': 'Game not found'}, 404
    game.delete()
    return '', 204

def generate_uuid(length):
    """
    Generate a uuid starting with 'm'
    :param length: Length of UUID plus a starting 'm'
    :return string: A string starting with 'm' plus length - 1 random chars (Base62)
    """
    chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
    return 'm' + ''.join(random.choices(chars, k=length - 1))


def findpairsum(arr):

    hash=set()

    for i in range(len(arr)-1):
        for j in range(i+1,len(arr)):

            sum=arr[i]+arr[j]

            if sum in hash:
                return True
            else:
                hash.add(sum)
    return False
arr=[2,9,3,5,8,6,4]
res=findpairsum(arr)
print(res)


import os
import numpy as np
from matplotlib import pyplot as plt
from scipy.ndimage.interpolation import rotate
import pydicom as dicom
import math
import re

class DCMDataLoader(object):
    def __init__(self, LDCT_image_path, NDCT_image_path, \
                 image_size=512, patch_size=64, depth=1,batch_size=32):
        # dicom file dir
        self.LDCT_image_path = LDCT_image_path
        self.NDCT_image_path = NDCT_image_path
        # image params
        self.image_size = image_size
        self.patch_size = patch_size
        self.depth = depth
        # training params
        self.batch_size = batch_size
        # CT slice name
        self.LDCT_image_name, self.NDCT_image_name = [], []
        # batch generator  prameters
        print('load case mean and std... ')
        self.case_mean_l = np.load("case_mean_l.npy")
        self.case_std_l = np.load("case_std_l.npy")
        self.patient_l = np.load("patient_l.npy")
        self.max_l = np.load("max_l.npy")
        self.min_l = np.load("min_l.npy")
        self.case_mean_h = np.load("case_mean_h.npy")
        self.case_std_h = np.load("case_std_h.npy")
        self.max_h = np.load("max_h.npy")
        self.min_h = np.load("min_h.npy")
        self.patient_h = np.load("patient_h.npy")

    def __call__(self, patent_no_list, LDCT_image_path, NDCT_image_path):
        p_LDCT = []
        p_NDCT = []
        # print('patent_no_list',patent_no_list)
        for patent_no in patent_no_list:
            print('patent_no', patent_no)
            P_LDCT_path = os.listdir(patent_no)
            print('P_LDCT_path', P_LDCT_path)
            for i in P_LDCT_path:
                if 'DIRFILE' in i:
                    P_LDCT_path.remove(i)
            patent_no_h = patent_no.replace(str(LDCT_image_path), str(NDCT_image_path))

            P_NDCT_path_r = os.listdir(patent_no_h)
            # if img not exit both in low_dose and high_dose path, the img will not be included in further analysis
            rem_p = []
            for j in range(len(P_LDCT_path)):
                if P_LDCT_path[j] not in P_NDCT_path_r:
                    rem_p.append(P_LDCT_path[j])
            for k in rem_p:
                P_LDCT_path.remove(k)

            P_NDCT_path = [i for i in range(len(P_LDCT_path))]
            for i in range(len(P_LDCT_path)):
                P_NDCT_path[i] = os.path.join(patent_no_h + '/' + P_LDCT_path[i])
                P_LDCT_path[i] = os.path.join(patent_no + '/' + P_LDCT_path[i])

            # load images
            # P_LDCT_path inculude all img path of one sample
            # CT slice name
            LDCT_slice_nm = self.get_slice_nm(P_LDCT_path, '{}_{}'.format(patent_no.split('\\')[-1], self.LDCT_image_path.split('\\')[-1]))
            NDCT_slice_nm = self.get_slice_nm(P_NDCT_path, '{}_{}'.format(patent_no_h.split('\\')[-1], self.NDCT_image_path.split('\\')[-1]))
            print('LDCT_slice_nm', LDCT_slice_nm)
            self.LDCT_image_name.extend(LDCT_slice_nm)
            self.NDCT_image_name.extend(NDCT_slice_nm)
            p_LDCT.append(P_LDCT_path)
            p_NDCT.append(P_NDCT_path)
            print('P_LDCT_path',len(P_LDCT_path))
            print('p_NDCT_path', len(P_NDCT_path))

        self.LDCT_images = np.concatenate(tuple(p_LDCT), axis=0)
        self.NDCT_images = np.concatenate(tuple(p_NDCT), axis=0)
        # image index
        self.LDCT_index, self.NDCT_index = list(range(len(self.LDCT_images))), list(range(len(self.NDCT_images)))
        np.random.seed(10)
        np.random.shuffle(self.LDCT_images)
        np.random.seed(10)
        np.random.shuffle(self.NDCT_images)

    def normalize(self, img, max_=3072, min_=-1024):
        img = img.astype(np.float32)
        img[img > max_] = max_
        img[img < min_] = min_
        img = (img - np.min(img)) / (np.max(img) - np.min(img))
        return img

    def normalization(self, path, case_mean_all, case_std_all, max_n, min_n, patient_list):
        dicom_array = dicom.read_file(path)
        dicom_image = self.get_pixels_hu(dicom_array)
        dicom_image[dicom_image > max_n] = max_n
        dicom_image[dicom_image < min_n] = min_n
        patient = re.split(r'[\\/]', path)[-2]
        patient_list = patient_list.tolist()
        index_p = patient_list.index(patient)
        mean_p = case_mean_all[index_p]
        std_p = case_std_all[index_p]
        dicom_image = (dicom_image - min_n)/(max_n-min_n)
        # dicom_image = self.normalize(dicom_image)
        # plt.imshow(dicom_image)
        # plt.show()
        return dicom_image

    def get_slice_nm(self, P_LDCT_path, patent_no):
        digit = 4
        slice_nm = []
        for slice_number in range(len(P_LDCT_path)):
            name = P_LDCT_path[slice_number].split('\\')[-1]
            # sorted(idx), sorted(d_idx)  -> [1, 10, 2], [ 0001, 0002, 0010]
            s_idx = str(slice_number)
            d_idx = '0' * (digit - len(s_idx)) + s_idx
            slice_nm.append(patent_no + '_' + name + '_' + d_idx)
        return slice_nm

    def get_pixels_hu(self, slice, pre_fix_nm=''):
        image = slice.pixel_array
        image = image.astype(np.int16)
        image[image == -2000] = 0
        intercept = slice.RescaleIntercept
        slope = slice.RescaleSlope
        if slope != 1:
            image = slope * image.astype(np.float32)
            image = image.astype(np.int16)
        image += np.int16(intercept)
        return np.array(image, dtype=np.int16)

    def test_img(self, LDCT_slice, NDCT_slice):
        LDCT_image = self.normalization(LDCT_slice, self.case_mean_l, self.case_std_l, self.max_l, self.min_l, self.patient_l)
        NDCT_image = self.normalization(NDCT_slice, self.case_mean_h, self.case_std_h, self.max_h, self.min_h, self.patient_h)
        return LDCT_image, NDCT_image

    # WGAN_VGG, RED_CNN
    def get_randam_patches(self, LDCT_slice, NDCT_slice, patch_size, whole_size=512):
        # truncate & normalization
        LDCT_image = self.normalization(LDCT_slice, self.case_mean_l, self.case_std_l, self.max_l, self.min_l, self.patient_l)
        NDCT_image = self.normalization(NDCT_slice, self.case_mean_h, self.case_std_h, self.max_h, self.min_h, self.patient_h)
        whole_h = whole_w = whole_size
        h = w = patch_size
        # patch image range
        hd, hu = h // 2, int(whole_h - np.round(h / 2))
        wd, wu = w // 2, int(whole_w - np.round(w / 2))
        h_pc = np.random.choice(range(hd, hu + 1))
        w_pc = np.random.choice(range(wd, wu + 1))
        if len(LDCT_image.shape) == 3:  # 3d patch
            LDCT_patch = LDCT_image[:, h_pc - hd: int(h_pc + np.round(h / 2)), w_pc - wd: int(w_pc + np.round(h / 2))]
            NDCT_patch = NDCT_image[:, h_pc - hd: int(h_pc + np.round(h / 2)), w_pc - wd: int(w_pc + np.round(h / 2))]
        else:  # 2d patch
            LDCT_patch = LDCT_image[h_pc - hd: int(h_pc + np.round(h / 2)), w_pc - wd: int(w_pc + np.round(h / 2))]
            NDCT_patch = NDCT_image[h_pc - hd: int(h_pc + np.round(h / 2)), w_pc - wd: int(w_pc + np.round(h / 2))]
        return LDCT_patch, NDCT_patch

    def preproc_input(self, args):
        n_batches = int(len(self.LDCT_index)/(self.batch_size))
        print('n_batches', n_batches)
        for i in range(n_batches - 1):
            batch_LDCT = self.LDCT_images[i * self.batch_size:(i + 1) * self.batch_size]
            batch_NDCT = self.NDCT_images[i * self.batch_size:(i + 1) * self.batch_size]
            LDCT_imgs, NDCT_imgs = [], []
            for img_index in range(len(batch_LDCT)):
                mean_n = -1
                if self.patch_size != self.image_size:
                    while mean_n < -0.4:
                        LDCT_image, NDCT_image = self.get_randam_patches(batch_LDCT[img_index],batch_NDCT[img_index], args.patch_size)
                        mean_n = np.mean(LDCT_image)
                else:
                    LDCT_image = self.normalization(batch_LDCT[img_index], self.case_mean_l, self.case_std_l,
                                                self.patient_l)
                    NDCT_image = self.normalization(batch_NDCT[img_index], self.case_mean_h, self.case_std_h,
                                                self.patient_h)
                # print('mean_low', np.mean(LDCT_image))
                # plt.imshow(LDCT_image, cmap='gray')
                # plt.show()
                # plt.imshow(NDCT_image, cmap='gray')
                # plt.show()

                LDCT_imgs.append(np.expand_dims(LDCT_image, axis=-1))
                NDCT_imgs.append(np.expand_dims(NDCT_image, axis=-1))
            yield np.array(LDCT_imgs), np.array(NDCT_imgs)

# psnr
def log10(x):
    numerator = tf.log(x)
    denominator = tf.log(tf.constant(10, dtype=numerator.dtype))
    return numerator / denominator

def tf_psnr(img1, img2, PIXEL_MAX=255.0):
    mse = tf.reduce_mean((img1 - img2) ** 2)
    if mse == 0:
        return 100
    return 20 * log10(PIXEL_MAX / tf.sqrt(mse))

def psnr(img1, img2, PIXEL_MAX=255.0):
    mse = np.mean((img1 - img2) ** 2)
    if mse == 0:
        return 100
    return 20 * math.log10(PIXEL_MAX / math.sqrt(mse))

# save mk img
def save_image(LDCT, NDCT, output_, save_dir='.', max_=1, min_=0):
    f, axes = plt.subplots(2, 3, figsize=(30, 20))

    axes[0, 0].imshow(LDCT, cmap=plt.cm.gray, vmax=max_, vmin=min_)
    axes[0, 1].imshow(NDCT, cmap=plt.cm.gray, vmax=max_, vmin=min_)
    axes[0, 2].imshow(output_, cmap=plt.cm.gray, vmax=max_, vmin=min_)

    axes[1, 0].imshow(NDCT.astype(np.float32) - LDCT.astype(np.float32), cmap=plt.cm.gray, vmax=max_, vmin=min_)
    axes[1, 1].imshow(NDCT - output_, cmap=plt.cm.gray, vmax=max_, vmin=min_)
    axes[1, 2].imshow(output_ - LDCT, cmap=plt.cm.gray, vmax=max_, vmin=min_)

    axes[0, 0].title.set_text('LDCT image')
    axes[0, 1].title.set_text('NDCT image')
    axes[0, 2].title.set_text('output image')

    axes[1, 0].title.set_text('NDCT - LDCT  image')
    axes[1, 1].title.set_text('NDCT - outupt image')
    axes[1, 2].title.set_text('output - LDCT  image')
    if save_dir != '.':
        f.savefig(save_dir)
        plt.close()

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

# argparser string -> boolean type
def ParseBoolean(b):
    b = b.lower()
    if b == 'true':
        return True
    elif b == 'false':
        return False
    else:
        raise ValueError('Cannot parse string into boolean.')

# argparser string -> boolean type
def ParseList(l):
    return l.split(',')


import socket
import string
import random
import pandas as pd

HOST = '192.168.100.68'
PORT = 55555

#Faz a criptografia
def encryption(data):
    coded = ''
    hash = len(data)-1
    i = hash
    while i>= 0:
        coded = coded + random.choice(string.ascii_letters)
        coded = coded + data[i]
        coded = coded + random.choice(string.ascii_letters+string.digits)
        i -= 1
    return coded

#Faz a decriptografia
def decryption(data):
    decoded = ''
    hash = len(data)-1
    i = hash
    i -= 1
    while i >= 0:
        decoded = decoded + data[i]
        i -= 3
    return decoded

# codifica a string criptografada de ASCII para 8B6T
def encode(data):
    encoded = ''
    table = pd.read_csv('8b6tTABLE.csv')
    table.fillna(' ', inplace = True)
    for i in range(len(data)):
        for j in range(len(table)):
            if(table['Char'].iloc[j] == data[i]):
                encoded = encoded + flip(table['6T'].iloc[j])
    return encoded

# decodifica de 8b6T para ASCII
def decode(data):
    decoded = ''
    table = pd.read_csv('8b6tTABLE.csv')
    table.fillna(' ', inplace = True)
    i = 0
    k = 6
    while k < len(data):
        for j in range(len(table)):
            if(table['6T'].iloc[j] == flip(data[i:k])):
                decoded = decoded + table['Char'].iloc[j]
                i += 6
                k += 6
    return decoded

# muda os valores do sinal se tiver dois positivos seguidos
def flip(data):
    flipped = ''
    counter = 0
    for i in range(len(data)):
        if data[i] == '+':
            flipped = flipped + '-'
            counter += 1
        elif data[i] == '-':
            flipped = flipped + '+'
            counter -= 1
        else:
            flipped = flipped + '0'
    if counter >= 1:
        return flipped
    if counter <= -1:
        return flipped
    else: return data

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) #TCP
s.connect((HOST, PORT))

mensagem = "Só doido nesse curso!"
mensagem = encryption(mensagem)
mensagem = encode(mensagem)

s.sendall(str.encode(mensagem))

data = s.recv(1024)

print('Mensagem ecoada:', data.decode())


from asyncio.log import logger
import uvicorn
import logging
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

# Import services and DB connections here
from app.db.connections import db

"""Setting up application context"""
app = FastAPI()
# global logger = logging.getLogger(__name__)

@app.on_event("startup")
async def start_up():
    await db.init_dbi()

@app.on_event("shutdown")
async def shut_down():
    await db.close_dbi()

"""Enable CORS"""
app.add_middleware(
    CORSMiddleware,
    allow_origins=["http://localhost:5000"],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)
"""Root Page"""
@app.get("/")
def home():
    return "Home Page"

"""Main method call"""
if __name__ == "__main__":
    logging.config.fileConfig(fname='logger.ini')
    uvicorn.run(app, host="0.0.0.0", port=5000)


from sqlalchemy import Column, ForeignKey, Integer, String, DateTime, BigInteger, Float
from sqlalchemy.dialects.mysql import BIGINT
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from requests import get
from datetime import datetime
from time import sleep
from web3 import Web3

Base = declarative_base()

class Token(Base):
    __tablename__ = 'tokens'
    id = Column(Integer, primary_key=True)
    address = Column(String(42))
    name = Column(String(16))
    usdc_price = Column(Integer, nullable=True)

class Holder(Base):
    __tablename__ = 'holders'
    id = Column(Integer, primary_key=True)
    address = Column(String(42))
    code = Column(Integer, nullable=True)
    tx_count = Column(Integer, nullable=True)
    first_tx = Column(DateTime, nullable=True)
    last_tx = Column(DateTime, nullable=True)
    eth_balance = Column(Float, nullable=True)

class Balance(Base):
    __tablename__ = 'balances'
    id = Column(Integer, primary_key=True)
    holder = relationship(Holder)
    holder_id = Column(Integer, ForeignKey('holders.id'))
    token = relationship(Token)
    token_id = Column(Integer, ForeignKey('tokens.id'))
    tx_count = Column(Integer, nullable=True)
    balance = Column(BigInteger, nullable=True)
    first_tx = Column(DateTime, nullable=True)
    last_tx = Column(DateTime, nullable=True)

class EtherScanParser():
    def __init__(self, db_session, token):
        self.sess = db_session
        self.token = token

    def populate_addresses_from_transfer_logs_by_block_interval(self, from_block, to_block):
        result = []
        for i in range(0, 100):
            while True:
                try:
                    response = get(
                        "https://api.etherscan.io/api?module=logs&action=getLogs&fromBlock=%s&toBlock=%s&address=%s&topic0=0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef" % (
                        from_block, to_block, self.token.address)).json()
                except Exception as e:
                    sleep(10)
                    print("Error polling etherscan %s" % e)
                    continue
                break
        if response["message"] == "OK":
            for log_entry in response["result"]:
                timestamp = datetime.fromtimestamp(int(log_entry["timeStamp"], 16))
                address1 = Web3.toChecksumAddress("0x" + log_entry["topics"][1][-40:])
                address2 = Web3.toChecksumAddress("0x" + log_entry["topics"][1][-40:])
                for addr in [address1, address2]:
                    holder = self.sess.query(Holder).filter(Holder.address == addr).one_or_none()
                    if not holder:
                        holder = Holder(address = addr, tx_count = 1, first_tx = timestamp, last_tx = timestamp)
                        self.sess.add(holder)
                    else:
                        if holder.first_tx > timestamp:
                            holder.first_tx = timestamp
                        if holder.last_tx < timestamp:
                            holder.last_tx = timestamp

                    token_balance = self.sess.query(Balance).filter(Balance.holder == holder).filter(Balance.token == self.token).one_or_none()
                    if not token_balance:
                        token_balance = Balance(holder=holder, token = self.token, tx_count = 1, first_tx = timestamp, last_tx = timestamp)
                        self.sess.add(token_balance)
                    else:
                        if token_balance.first_tx > timestamp:
                            token_balance.first_tx = timestamp
                        if token_balance.last_tx < timestamp:
                            token_balance.last_tx = timestamp
                        token_balance.tx_count += 1
                    self.sess.commit()
        if response and response["result"] and len(response["result"]) > 0:
            retval = int(response["result"][-1]["blockNumber"],16)
        else:
            retval = from_block
        return retval

    def populate_addresses_from_transfer_logs_from_block(self, start_block):
        start_block = start_block
        while True:
            prev_start_block = start_block
            start_block = self.populate_addresses_from_transfer_logs_by_block_interval(start_block + 1, "latest")
            if start_block == prev_start_block + 1:
                return

"""
class Web3Walker():
    def __init__(self, db_session, w3):
        self.sess = db_session
        self.w3 = w3
    def update_eth_balance(self, holder):
        assert type(holder) == Holder
        holder.eth_balance = self.w3.eth.getBalance(holder.address)/1000000000000000000
        self.sess.commit()
    def update_token_balance(self, token):
        assert type(token) == Token
        pass
    def update_tx_count(self, holder):
        assert type(holder) == Holder
        holder.tx_count = self.w3.eth.getTransactionCount(holder.address)
        self.sess.commit()
    def update_code_size(self, holder):
        assert type(holder) == Holder
        holder.code = len(self.w3.eth.getCode(holder.address))
        self.sess.commit()
    def update_holder(self, holder):
        self.update_eth_balance(holder)
        self.update_tx_count(holder)
        self.update_code_size(holder)
"""


import os
import cv2
import numpy as np

from datetime import datetime
from uuid import uuid4

from PIL import Image
from telegram import Bot

from utils.landmarks_detector import LandmarksDetector
from utils.face_alignment import image_align

from pix2pix import Pix2pixModels

def handle_file_upload(update, context):
    print(f'handle new image {datetime.now().isoformat()}')

    chat_id = update.message.chat.id
    bot: Bot = context.bot

    file_path = f"pictures/{uuid4()}.jpeg"
    with open(file_path, "wb") as fd:
        context.bot.get_file(update.message.photo[-1]).download(out=fd)

    aligned_image_files_path = process_input_image(file_path)

    if not aligned_image_files_path:
        bot.send_message(chat_id=chat_id, text="I'm sorry, but I did not find any faces on the image")

    for aligned_image_file in aligned_image_files_path:

        for output_image in Pix2pixModels.inference(aligned_image_file):
            opencv_output_image = cv2.cvtColor(np.array(output_image), cv2.COLOR_RGB2BGR)
            resized_image = cv2.resize(opencv_output_image, (512, 512))

            output_image = cv2.cvtColor(resized_image, cv2.COLOR_BGR2RGB)
            output_image = Image.fromarray(output_image)

            webp_file_path = f"pictures/{uuid4()}.webp"
            output_image.save(webp_file_path, "webp")

            with open(webp_file_path, "rb") as fd:
                bot.send_sticker(chat_id, fd)

            os.remove(webp_file_path) # clear

        os.remove(aligned_image_file)

def process_input_image(input_file_path):
    landmarks_detector = LandmarksDetector()
    input_image = Image.open(input_file_path)

    output_files = []
    for face_landmarks in landmarks_detector.get_landmarks(input_file_path):
        output_image = image_align(input_image, face_landmarks, 256)
        output_file_path = f'pictures/{uuid4()}.jpeg'
        output_image.save(output_file_path, 'jpeg')
        output_files.append(output_file_path)

    return output_files


import pandas as pd
import numpy as np

from sklearn.model_selection import KFold
from sklearn.preprocessing import StandardScaler

from train import train_model

def k_fold_train_model(X: pd.DataFrame, y: pd.DataFrame, k: int, model: str, verbose: bool = True):
    all_train_acc = []
    all_test_acc = []

    # Choose only numeric columns
    X = X.select_dtypes(include=['number'])

    # Split to k folds
    kf = KFold(n_splits=k, shuffle=True, random_state=42)
    for train_index, test_index in kf.split(X):
        X_train, X_test = X.iloc[train_index], X.iloc[test_index]
        y_train, y_test = y.iloc[train_index], y.iloc[test_index]

        scaler = StandardScaler()
        scaler.fit(X_train)
        X_train = scaler.transform(X_train)
        X_test = scaler.transform(X_test)

        # Replace NaN with 0
        X_train[np.isnan(X_train)] = 0
        X_test[np.isnan(X_test)] = 0

        X_train = pd.DataFrame(X_train, columns=X.columns)
        X_test = pd.DataFrame(X_test, columns=X.columns)

        train_acc, test_acc, _ = train_model(X_train, X_test, y_train, y_test, model, verbose=verbose)

        all_train_acc.append(train_acc)
        all_test_acc.append(test_acc)

    if verbose:
        print(f'Model: {model}, Average training accuracy: {np.mean(all_train_acc)}, Average test accuracy: {np.mean(all_test_acc)}')

        print("Entire training set training:")
    scaler = StandardScaler()
    scaler.fit(X)
    all = scaler.transform(X)
    all[np.isnan(all)] = 0

    X = pd.DataFrame(all, columns=X.columns)
    _, _, clf = train_model(X, X, y, y, model, verbose=verbose)

    return np.mean(all_train_acc), np.mean(all_test_acc), clf


import logging
from collections import namedtuple
from unittest.mock import ANY, Mock, PropertyMock, patch

from django.test import TestCase
from django.urls import reverse

from api.models import Status, Tenant

class StatusModelTest(TestCase):
    """Tests against the status functions."""

    @classmethod
    def setUpClass(cls):
        """Test Class setup."""
        # remove filters on logging
        logging.disable(logging.NOTSET)
        cls.status_info = Status()

    @classmethod
    def tearDownClass(cls):
        """Test Class teardown."""
        # restore filters on logging
        logging.disable(logging.CRITICAL)

    def setUp(self):
        """Create test case setup."""
        super().setUp()
        t, created = Tenant.objects.get_or_create(tenant_name="public")
        if created:
            t.ready = True
            t.save()

    @patch("os.environ")
    def test_commit_with_env(self, mock_os):
        """Test the commit method via environment."""
        expected = "buildnum"
        mock_os.get.return_value = expected
        result = self.status_info.commit
        self.assertEqual(result, expected)

    @patch("subprocess.run")
    @patch("api.status.model.os.environ")
    def test_commit_with_subprocess(self, mock_os, mock_subprocess):
        """Test the commit method via subprocess."""
        expected = "buildnum"
        run = Mock()
        run.stdout = b"buildnum"
        mock_subprocess.return_value = run
        mock_os.get.return_value = None
        result = self.status_info.commit
        self.assertEqual(result, expected)

class StatusViewTest(TestCase):
    """Tests the status view."""

    def test_status_endpoint(self):
        """Test the status endpoint."""
        url = reverse("server-status")
        response = self.client.get(url)
        self.assertEqual(response.status_code, 200)


import random, blackjack_art as ba
from os import system

# Create the cards with 6 decks.
set_cards = ['A', 'J', 'Q', 'K'] + list(range(2, 11))
cards = set_cards * 24

def deal_card(hand):
    '''Deals a card and sets it in hand. It returns the total value of the cards in hand.'''
    global cards
    card = random.choice(cards)
    hand.append(card)
    cards.remove(card)
    return hand_score(hand)

def hand_score(hand):
    '''Calculates the total value of hand and returns it as an int.'''
    sum = 0
    for n in hand:
        if n in ['J', 'Q', 'K']:
            sum += 10
        elif n == 'A':
            if sum < 11:
                sum += 11
            else:
                sum += 1
        else:
            sum += n
    return sum

def show_hand(hand, human = True, final = False):
    '''Prints the hand showing the cards for the player (human=True) or the dealer (human=False) and the score (final=True).'''
    text = 'final' if final else 'current'
    if human:
        print(f'    Your cards: {hand}, {text} score: {hand_score(hand)}')
    else:
        show = hand if final else f'[{hand[0]}]'
        text = f'    Dealer cards: {show}'
        if final:
            text += f', final score: {hand_score(hand)}'
        print(text)

finish = False
win_human, win_comp, draws = 0, 0, 0

print(ba.BLACKJACK_LOGO)
print('Welcome to blackjack!')
while not finish:
    hand_human, hand_comp = [], []
    # First, deliver a card to player, one to dealer, one to player and another one to dealer
    deal_card(hand_human)
    deal_card(hand_comp)
    # Calculate the score both players have
    score_human = deal_card(hand_human)
    score_comp = deal_card(hand_comp)

    # Showing hands, only one for the dealer
    show_hand(hand_human)
    show_hand(hand_comp, human=False)

    stop_game = False
    while not stop_game:
        more = input('Get another card (h) or stand (s)?: ')
        while more not in ['h', 's']:
            more = input('Only type h or s: ')

        # Hit the player with another card
        if more == 'h':
            score_human = deal_card(hand_human)
            show_hand(hand_human)
            show_hand(hand_comp, human=False)
            # If the player exceeds 21, the game is over
            if score_human > 21:
                stop_game = True
                print('You lost!')
                win_comp += 1

        # Stand and let the dealer play, then show the results
        elif more == 's':
            stop_game = True
            while score_comp < 17:
                score_comp = deal_card(hand_comp)
            show_hand(hand_human, final=True)
            show_hand(hand_comp, human=False, final=True)

            # The different outcomes
            if score_comp > 21:
                print('You win')
                win_human += 1
            elif score_human == score_comp:
                print('Draw')
                draws += 1
            elif score_comp > score_human:
                print('You lost')
                win_comp += 1
            elif score_comp < score_human:
                print('You win')
                win_human += 1
            else:
                print('I missed this case, please report', score_human, score_comp)

        # The game stopped, show accumulated results
        if stop_game:
            print(f'Score is you: {win_human}, dealer: {win_comp}, and draws: {draws}')
            print()
            # Play again?
            one_more = input('Play again? y or n: ')
            while one_more not in ['y', 'n']:
                one_more = input('Play again? Only type y or n: ')
            if one_more == 'y':
                system('clear')
            else:
                print('Thank you, and goodbye!')
                finish = True