""" Variable exmaples
Created by Chip Huyen (chiphuyen@cs.stanford.edu)
CS20: "TensorFlow for Deep Learning Research"
cs20.stanford.edu
Lecture 02
"""
import os
os.environ['TF_CPP_MIN_LOG_LEVEL']='2'

import numpy as np
import tensorflow as tf

# Example 1: creating variables
s = tf.Variable(2, name='scalar')
m = tf.Variable([[0, 1], [2, 3]], name='matrix')
W = tf.Variable(tf.zeros([784,10]), name='big_matrix')
V = tf.Variable(tf.truncated_normal([784, 10]), name='normal_matrix')

s = tf.get_variable('scalar', initializer=tf.constant(2))
m = tf.get_variable('matrix', initializer=tf.constant([[0, 1], [2, 3]]))
W = tf.get_variable('big_matrix', shape=(784, 10), initializer=tf.zeros_initializer())
V = tf.get_variable('normal_matrix', shape=(784, 10), initializer=tf.truncated_normal_initializer())

with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    print(V.eval())

# Example 2: assigning values to variables
W = tf.Variable(10)
W.assign(100)
with tf.Session() as sess:
    sess.run(W.initializer)
    print(sess.run(W))                    	# >> 10

W = tf.Variable(10)
assign_op = W.assign(100)
with tf.Session() as sess:
    sess.run(assign_op)
    print(W.eval())                     	# >> 100

# create a variable whose original value is 2
a = tf.get_variable('scalar', initializer=tf.constant(2))
a_times_two = a.assign(a * 2)
with tf.Session() as sess:
    sess.run(tf.global_variables_initializer())
    sess.run(a_times_two)                 	# >> 4
    sess.run(a_times_two)                 	# >> 8
    sess.run(a_times_two)                 	# >> 16

W = tf.Variable(10)
with tf.Session() as sess:
    sess.run(W.initializer)
    print(sess.run(W.assign_add(10)))     	# >> 20
    print(sess.run(W.assign_sub(2)))     	# >> 18

# Example 3: Each session has its own copy of variable
W = tf.Variable(10)
sess1 = tf.Session()
sess2 = tf.Session()
sess1.run(W.initializer)
sess2.run(W.initializer)
print(sess1.run(W.assign_add(10)))        	# >> 20
print(sess2.run(W.assign_sub(2)))        	# >> 8
print(sess1.run(W.assign_add(100)))        	# >> 120
print(sess2.run(W.assign_sub(50)))        	# >> -42
sess1.close()
sess2.close()

# Example 4: create a variable with the initial value depending on another variable
W = tf.Variable(tf.truncated_normal([700, 10]))
U = tf.Variable(W * 2)


from collections import deque
import sys

n = int(sys.stdin.readline())
graph = [[] for _ in range(n + 1)]
visited = [False] * (n + 1)
parent = [[] for _ in range(n + 1)]

def bfs(x):
    queue = deque()
    queue.append(x)
    visited[x] = True
    while queue:
        a = queue.popleft()
        for i in graph[a]:
            if not visited[i]:
                visited[i] = True
                parent[i] = a
                queue.append(i)

for i in range(n-1):
    a, b = map(int, sys.stdin.readline().split())
    graph[a].append(b)
    graph[b].append(a)

for i in range(1, n+1):
    if not visited[i]:
        bfs(i)

for i in range(2, n+1):
    print(parent[i])


import argparse

import pytorch_lightning as pl
from omegaconf import OmegaConf

from Modules.data_lightning import DataModuleForSentenceClassification
# from Modules.MSP_lightning import ModelModule
from Modules.DMSP_lightning import ModelModule

if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument('--max_len', type=int, default=64)
    parser.add_argument('--batch_size', type=int, default=64)
    parser.add_argument('--max_epoch', type=int, default=100)
    parser.add_argument('--ckpt', type=str, default=None)
    parser.add_argument('--is_train', type=str, default='Y')
    args = parser.parse_args()
    pl.seed_everything(1)
    """load config.yml"""
    config = OmegaConf.load("config.yml")
    dataset_conf, model_conf = config.dataset, config.model
    dm = DataModuleForSentenceClassification(file_path_dir=[dataset_conf.filename, dataset_conf.subset], max_len=args.max_len,
                                             batch_size=args.batch_size, label_map=dataset_conf.label_map)
    model = ModelModule(**model_conf, epsilon=0.5, patience=2)
    trainer = pl.Trainer(
        callbacks=[
            pl.callbacks.ModelCheckpoint(
                monitor="validation_step_accuracy",
                save_top_k=2,
                filename='{epoch}-{validation_step_loss:.4f}-{validation_step_accuracy:.4f}',
                mode='max'
            )
        ],
        accelerator="auto",
        devices='auto',
        max_epochs=args.max_epoch,
        check_val_every_n_epoch=1,
        log_every_n_steps=1
    )

    if args.is_train == "Y":
        dm.setup('fit')
        train_loader = dm.train_dataloader()
        val_loader = dm.val_dataloader()
        if args.ckpt:
            trainer.fit(model, train_loader, val_loader, ckpt_path=args.ckpt)
        else:
            trainer.fit(model, train_loader, val_loader)
    else:
        dm.setup('test')
        test_loader = dm.test_dataloader()
        trainer.test(model, test_loader, args.ckpt)


from spddo.subs import model
from blueshed.micro.orm.orm_utils import serialize

def filter_services(context: 'micro_context', term='', offset=0, limit=10, id=None):
    with context.session as session:
        if id is not None:
            service = session.query(model.Service).get(id)
            result = serialize(service)
            return result
        term = "{}%".format(term)
        services = session.query(model.Service).\
            filter(model.Service.name.like(term))

        return [serialize(service) for service in services]


from collections import deque
import sys
sys.setrecursionlimit(10 ** 6)
input = sys.stdin.readline

n, m = map(int, input().split())
a = [input().rstrip() for _ in range(n)]
dr = (0, 1, 0, -1)
dc = (1, 0, -1, 0)
visited = [[0] * m for _ in range(n)]

def bfs():
    visited[0][0] = 1
    q = deque([(0, 0)])
    while q:
        r, c = q.popleft()
        if r == n - 1 and c == m - 1:
            return visited[n - 1][m - 1]
        for i in range(4):
            nr = r + dr[i]
            nc = c + dc[i]
            if 0 <= nr < n and 0 <= nc < m:
                if visited[nr][nc]: continue
                if a[nr][nc] == '0': continue
                visited[nr][nc] = visited[r][c] + 1
                q.append((nr, nc))
print(bfs())


# First import the files like this

from AI.sources.ressources.stone import *
from AI.sources.ressources.stonesTypes import *
from AI.sources.ressources.dictionaries import *
from AI.sources.elevation.playerNeededForElevation import *
from AI.sources.socket.socket import *

def exemple():
    # creation of an obsject s with class Stone
    # Usage: obj = Stone(StonesTypes.STONE) by default stone type is unknow.

    s = Stone(StonesTypes.LINEMATE)

    # logStone print stone type

    s.logStone()

    # You can get the number of ressources required by using dictionaries
    # Usage : nameofthestone_{desirable|necessary}[level] you get an int

    print(StonesTypes.LINEMATE, "desirable From level 5 to level 6  =", linemate_desirable[5])

    # You can get the number of player necessary for an elevation by calling  playerNeededForElevation dictionary
    # Usage:  playerNeededForElevation[level]
    print("From level 2 to level 3 you need", playerNeededForElevation[2], "players")

def exemple_socket():

    # create a new object socket:
    # Usage: obj = Socket(host = "localhost", port = 3630)
    # ecah Socket obj get an unique ID
    client = Socket()

    # get Socket Infos:
    print (client.getInfo())

    # connect to server
    ret = client.connect()

    if ret == 0:
        #send a message
        client.sendMessage("CouCou Niels <3")

        #recive Data
        client.getData()

        #close connection
        client.closeConection()

    # all Socket method which need connections return 84 in errors case

if __name__ == "__main__":
    exemple()
    exemple_socket()


import socket
import re
from threading import Thread
import threading
import FSChunk
import FastFileServeTable
import pickle
import math
import constants
import http.client

# Os prints começados por '++' são referentes à ligação UDP, e os '--' são TCP

fastFileServList = FastFileServeTable.FastFileServeTable()     # 'ffs': ['(file,size)']     -> Lista de FFSs com uma lista de files
listaPedidos     = FastFileServeTable.ListaPedidos()           # 'iphttp': 'filename'       -> Lista de pedidos de ficheiros
fileToGo         = b''

# Faz a ligação UDP
def UDPListen(lock):
    IP_ADDRESS  = "127.0.0.1"
    UDP_PORT_NO = 8888

    UDPServerSocket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # IPv4, UDP
    UDPServerSocket.bind((IP_ADDRESS, UDP_PORT_NO))

    print("++HTTPGateway a escutar em UDP com sucesso")

    while True:

        # Recebe uma mensagem por UDP de um novo FFS
        data, addr = UDPServerSocket.recvfrom(constants.MAX_BUFFER)
        mensagem = pickle.loads(data).data
        nrMsg, files = re.split("__",mensagem.decode("utf-8"))
        print("++Message",mensagem)

        # Caso os files não caibam numa mensagem, ler até ter os files todos
        while int(nrMsg) > 1:
            data2, addr = UDPServerSocket.recvfrom(constants.MAX_BUFFER)
            _ , files2 = re.split("__",pickle.loads(data2).data.decode("utf-8"))
            files += files2
            nrMsg-=1
            print("++ Mais 1")

        # Adiconar files e FFS
        files = files[1:-1]
        files_tuples = list(eval(files))
        fastFileServList.adicionaFFS(addr[0],addr[1],files_tuples)

        print(f"++FastFileServ {addr} Ligado", nrMsg, files_tuples)
        print("++Lista de servers", fastFileServList.servidores)

        # Trata pedidos
        print("++Lista Pedidos", listaPedidos.isNotEmpty())

        if listaPedidos.isNotEmpty():

            print("++ENTROU")
            # Vai buscar o filename do pedido de cima
            f = listaPedidos.primeiroPedido()
            #print(f)

            # Vê que ffs têm o file para transferir
            disponiveis = fastFileServList.procuraFile(f)
            print("++Disponiveis:", disponiveis)

            # Selecionar um ffs para pedir um file
            ffs_selecionado = disponiveis[0]
            pedir_file = "Ficheiro: " + f
            print("++Cenas",pedir_file, ffs_selecionado)
            file_bytes = pedir_file.encode()
            fschunk = FSChunk.FSChunk(IP_ADDRESS,ffs_selecionado,file_bytes)
            UDPServerSocket.sendto(pickle.dumps(fschunk), (ffs_selecionado[0],ffs_selecionado[1].porta))

            chunkList = []
            # Receber o file
            i = fastFileServList.tamanhoFile(addr[0],f)
            print(i)

            nrMsg = math.ceil(i/constants.MAX_CHUNKSIZE)

            while nrMsg > 0:
                data, addr = UDPServerSocket.recvfrom(constants.MAX_BUFFER)
                msg = pickle.loads(data).data.decode("utf-8")
                chunkList.append(msg)
                nrMsg -= 1

            # Tratar das exceções caso o ficheiro não chegue com sucesso
            # Verificar nr de msg recebidas e verificar nr de bytes:
                # Se estiver OK, continuar
                # Se não, voltar a pedir a outro FFS

            print("++fora")

            # Dar locks e finalizar file
            lock.acquire()
            teste = ''.join([str(elem) for elem in chunkList])
            fileTeste = teste
            fileToGo = teste.encode("utf-8")
            lock.release()

            # Remover Pedido da lista
            listaPedidos.removePedido(ffs_selecionado[0])
            print("++ Removido pedido", fileToGo)

# Faz a ligação TCP
def TCPListen(lock):

    IP_ADDRESS  = "localhost"
    TCP_PORT_NO = 8080

    TCPServerSocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # IPv4, TCP
    TCPServerSocket.bind((IP_ADDRESS, TCP_PORT_NO))

    print("--HTTPGateway a escutar em TCP com sucesso")

    while True:
        # Faz a conexão TCP
        TCPServerSocket.listen(1)
        conn, address = TCPServerSocket.accept()
        print("--TCP connection from", address)

        # Parsing do HTTP request
        data = conn.recv(constants.MAX_BUFFER)
        stringdata = data.decode('utf-8')
        filename = re.sub('/','',stringdata).split(' ')
        ip = f"{address[0]}"#":{address[1]}"
        print("--Filename Requested:", filename[1],ip)

        # Acrescentar pedido à listaPedidos
        listaPedidos.adicionaPedido(ip,TCP_PORT_NO,filename[1])

        print("-- Vamos esperar")
        # Verificar o filesToGo até ter lá o ficheiro
        while listaPedidos.procuraPedido(ip):
           1

        print("-- Parei de esperar")

        # Ler o ficheiro e enviar o ficheiro
        # client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        # client_socket.connect((ip,constants.TCP_PORT))

        # BODY = "***filecontents***"
        # print("aquiaaaaaaa")
        # conn = http.client.HTTPConnection("localhost", 8080)
        # print(str(conn))
        # conn.request("PUT", "/teste.txt", BODY)
        # print("aquiaaaaaaabbbbbbbbb")
        # response = conn.getresponse()

        # print(response.status, response.reason)

        lock.acquire()

        # print("Vamos enviar?", address, str(conn), fileToGo)
        conn.send(fileToGo)

        lock.release()

        conn.close()

def main():
    global fileToGo

    fileToGo = b''

    lock = threading.Lock()

    ThreadUDP = Thread(target=UDPListen, args=(lock,))
    ThreadTCP = Thread(target=TCPListen, args=(lock,))

    ThreadUDP.start()
    ThreadTCP.start()

    print("Servidor Ligado!")

if __name__ == "__main__":
    main()


from django.urls import path

# importing views
from .views import (
    AllPizzaAPI,
    PizzaDetailAPI,
)

urlpatterns = [
    path('pizza', AllPizzaAPI.as_view(), name='list-pizza'),
    path('pizza/<pk>', PizzaDetailAPI.as_view(), name='pizza-detail'),
]


# This file contains code for suporting addressing questions in the data

"""# Here are some of the imports we might expect
import sklearn.model_selection  as ms
import sklearn.linear_model as lm
import sklearn.svm as svm
import sklearn.naive_bayes as naive_bayes
import sklearn.tree as tree

import GPy
import torch
import tensorflow as tf

# Or if it's a statistical analysis
import scipy.stats"""

from fynesse import access
from fynesse import assess
from fynesse.access import km_to_crs, crs_to_km, flatten
from shapely.geometry import Polygon, LineString, Point
from sklearn.decomposition import PCA
from sklearn.model_selection import train_test_split
from sklearn.model_selection import train_test_split

import geopandas as gpd
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import sklearn
import sklearn.metrics as skm
import statsmodels.api as sm
import warnings

warnings.filterwarnings("ignore", message= ".*Geometry is in a geographic CRS.*")

def add_feature_from_pois(gdf, pois, larger_gdf=None, **feature_kwargs):
  """
  Returns the geodataframe gdf, but with a new column with values for the feature
  specified in the feature_kwargs dictionary, which should contain entries

      func : name indicating aggregation function to calculate the feature's value
      pois_cond : lambda taking a pois and extracting the relevant POIs entries
      dist : number of kilometers to search for POIs
      name : name of the feature, and what to name the column

  The pois geodataframe should contain POIs from OSM where all the columns mentioned in
  pois_cond exists in pois. The POIs in `pois` should also have been fetched from an area
  around all the entries in `gdf`.
  """
  augmented_gdf = None
  if feature_kwargs['func'] == 'closest':
    augmented_gdf = assess.get_closest_poi_within_radius(gdf, feature_kwargs['pois_cond'](pois), feature_kwargs['dist'], feature_kwargs['name'])
  elif feature_kwargs['func'] == 'count':
    augmented_gdf = assess.get_num_pois_within_radius(gdf, feature_kwargs['pois_cond'](pois), feature_kwargs['dist'], feature_kwargs['name'])
  elif feature_kwargs['func'] == 'avg_dist':
    augmented_gdf = assess.get_average_dist_to_poi_within_radius(gdf, feature_kwargs['pois_cond'](pois), feature_kwargs['dist'], feature_kwargs['name'])
  elif feature_kwargs['func'] == 'num_houses':
    if larger_gdf is None:
      raise Exception("Must specify larger_gdf if using features 'num_houses'")
    augmented_gdf = assess.get_num_pois_within_radius(gdf, larger_gdf, feature_kwargs['dist'], feature_kwargs['name'])
  else:
    raise NotImplemented(f"Function {feature_kwargs['func']} is not implemented.")
  return augmented_gdf

def add_many_features_from_pois(gdf, pois, larger_gdf=None, **feature_kwargs):
  """
  A generalised version of add_feature_from_pois. The only difference is that
  the 'func' and 'name' keys in the feature specification may contain a list
  of functions to apply, e.g. 'func' : ['count', 'avg_dist']
  """
  if isinstance(feature_kwargs['func'], str):
    assert isinstance(feature_kwargs['name'], str), "If func is singular, name must be as well"
    return add_feature_from_pois(gdf, pois, larger_gdf, **feature_kwargs)
  else:
    # run the single function on all the provided funcs
    for func, name in zip(feature_kwargs['func'], feature_kwargs['name']):
      gdf = add_feature_from_pois(gdf, pois, larger_gdf=larger_gdf, **{
          'func': func,
          'pois_cond' : feature_kwargs['pois_cond'],
          'dist' : feature_kwargs['dist'],
          'name' : name
      })
    return gdf

def build_prices_coordinates_features_dataset(latitude, longitude, date,
                                              property_type, bb_size_km,
                                              pois_bb_size_km, year_range_size,
                                              pois_keys, features,
                                              logging=False):
  """
  Returns a tuple

      dataset_gdf, pois, larger_dataset_gdf

  dataset_gdf:
    a pandas dataframe with a row for every property sold within a
    bounding box of size `bb_size_km` around point (lat, lon) within a time
    window of `year_range_size` number of years, centred at year `date`.  This
    dataframe will have the columns of the prices_coordinates_table @see
    inner_join_sql_query and also have an extra column for each feature described
    in `features`. Rows of property types other than `property_type` are filtered out
  pois:
    a dataframe of OSM POIs found at (lat, lon) within a bounding box of size
    `pois_bb_size_km`, filtering out POIs with NaN values in all the keys in
    `pois_keys`
  larger_gdf:
    a pandas dataframe with the same columns as dataset_gdf, but containing
    all property types and sampling from a slightly larger bounding box. This dataframe
    is to be provided to @see add_feature_from_pois if the 'num_houses' feature is
    to computed

  :param latitude, longitude   geoposition around which to build dataset
  :param date                  integer specifying year around which data is fetched
  :param property_type         a valid property type in pp_data
  :param bb_size_km            size of the sides of the geographical bounding box in
                               which prices_coordinates data is fetched
  :param pois_bb_size_km       size of the side of the bounding box in which
                               which OSM data is fetched
  :param year_range_size       temporal bounding box, specified as integer indicating number of years
  :param pois_keys             OSMNX POIs keys to use when fetching POIs. These keys must be a superset
                               of the keys mentioned in any of the features
  :param features              A list of feature specifications @see add_feature_from_pois
  """

  if logging:
    print((f"Building prices coordinates with features dataset at point"
      f" ({longitude}, {latitude}) with bounding box size {bb_size_km}km"
      f" with POIs bounding box size {pois_bb_size_km}km "
      f"with features: {', '.join(flatten([f['name'] for f in features]))}"))

  bb_half_size = km_to_crs(bb_size_km) / 2
  year_diff    = int(year_range_size / 2)

  # Fetch prices_coordinate rows and
  #   cache SQL query for location and date range, so can be reused later
  cache_key_sql = ("prices_coordinates", latitude, longitude, date, bb_size_km, year_range_size)
  if cache_key_sql not in build_prices_coordinates_features_dataset.cache:
    if logging:
      print(f"The cache key {cache_key_sql} is not in cache, running SQL query...")
    build_prices_coordinates_features_dataset.cache[cache_key_sql] = assess.make_geodataframe(
      access.prices_coordinates_range_query_fast(longitude-bb_half_size,
                                      longitude+bb_half_size,
                                      latitude-bb_half_size,
                                      latitude+bb_half_size,
                                      [date-year_diff, date+year_diff])
      )
  elif logging:
    print(f"The cache key {cache_key_sql} is in cache, skipping SQL query...")
  prices_coordinates_gdf = build_prices_coordinates_features_dataset.cache[cache_key_sql]

  # filter out properties of different types, unless that would give too little data
  if len(prices_coordinates_gdf[prices_coordinates_gdf['property_type'] == property_type]) > 100:
    print(f"Found enough properties of type {property_type}. Filtering out other types for predictions...")
    prices_coordinates_gdf = prices_coordinates_gdf[prices_coordinates_gdf['property_type'] == property_type]
    print(f"Which leaves {len(prices_coordinates_gdf)} rows of property type {property_type}")
  else:
    print("Did not find enough properties of appropriate type. Using all types...")
    print("WARNING: This will likely cause the prediction to be very inaccurate!")

  # if no data is found, we must fail
  if len(prices_coordinates_gdf) == 0:
    return None, None, None

  if logging:
    print(f"Using {len(prices_coordinates_gdf)} prices_coordinate rows")

  # Fetch prices_coordinate rows that is used for num_houses feature and
  #   cache SQL query for location and date range, so can be reused later
  houses_dist = 0
  for f in features:
    if f['func'] == 'num_houses':
      houses_dist = max(f['dist'], houses_dist)
    elif f['func'] == ['num_houses']:
      houses_dist = max(f['dist'], houses_dist)
  larger_prices_gdf = None
  # we look at number of houses sold within some dist of houses in our dataset,
  #   so we need an extra dataset of houses sold which cover a larger region
  if houses_dist > 0:
    # Fetch prices_coordinates rows for larger range and put result in larger_prices_gdf, and
    #   use caching to speed up computation time
    cache_key_sql = ("prices_coordinates", latitude, longitude, date, bb_size_km + houses_dist, year_range_size)
    bb_larger_half_size = km_to_crs(bb_size_km + houses_dist)
    if cache_key_sql not in build_prices_coordinates_features_dataset.cache:
      if logging:
        print(f"The cache key {cache_key_sql} is not in cache, running SQL query...")
      build_prices_coordinates_features_dataset.cache[cache_key_sql] = assess.make_geodataframe(
          access.prices_coordinates_range_query_fast(longitude-bb_larger_half_size,
            longitude+bb_larger_half_size,
            latitude-bb_larger_half_size,
            latitude+bb_larger_half_size,
            [date-year_diff, date+year_diff])
          )
    elif logging:
      print(f"The cache key {cache_key_sql} is in cache, skipping SQL query...")
    larger_prices_gdf = build_prices_coordinates_features_dataset.cache[cache_key_sql]
    if logging:
      print(f"Found {len(larger_prices_gdf)} prices_coordinate rows of any property type for larger area")

  # fetch the POIs around the bounding box
  cache_key_osm = ("OSM", latitude, longitude, pois_bb_size_km)
  if cache_key_osm not in build_prices_coordinates_features_dataset.cache:
    if logging:
      print(f"The cache key {cache_key_osm} is not in cache, querying OSM...")
    build_prices_coordinates_features_dataset.cache[cache_key_osm] = \
      access.get_pois_around_point(longitude, latitude, pois_bb_size_km / 2, keys=pois_keys)
  else:
    print(f"The cache key {cache_key_osm} is in cache, skipping OSM query...")
  pois = build_prices_coordinates_features_dataset.cache[cache_key_osm]

  # The osmx API will not add column for specified key if it doesn't find any
  #   entities of that type, so we need to add it explicitly and fill it with NaNs
  #   to avoid ValueErrors later down the line when adding features
  for k in pois_keys:
    if k not in pois.columns:
      pois[k] = np.nan
  # print(pois.to_string()[0:10000000])

  # fail if nothing found
  if len(pois) == 0:
    return None, None, None

  if logging:
    print(f"Found {len(pois)} POIs in the area")

  # add the features
  for f in features:
    if (f['func'] == 'num_houses' or f['func'] == ['num_houses']) and len(prices_coordinates_gdf) > 1800:
      print(f"There was a lot of data entries found ({len(prices_coordinates_gdf)}). "
        + "Considering removing the feature num_houses to speed up computation")

    # Add a new column with name f['name'] using the values computed for the feature
    # prices_coordinates_gdf = prices_coordinates_gdf.assign(**{f['name']: build_prices_coordinates_features_dataset.cache[cache_key_f].to_numpy()})
    prices_coordinates_gdf = add_many_features_from_pois(prices_coordinates_gdf, pois, larger_prices_gdf, **f)
    print(f"Added feature {f['name']} to dataset")

  # return
  return prices_coordinates_gdf, pois, larger_prices_gdf
build_prices_coordinates_features_dataset.cache = {}

"""
##################### predict_price #################################
"""

def predict_price(latitude, longitude, date, property_type, build_dataset_kwargs, design, printSummary=False, plotAx=None):
  """
  Predicts the price of a property of type `property_type` to be bought around location (lat, lon)
  at a date around year `date`. The `build_dataset_kwargs` is a dictionary containing the keys:

      bb_size_km       : bounding box size in km for prices_coordinates data querying
      pois_bb_size_km  : bounding box size from which POIs are fetched from OSM
      year_range_size  : integer specifying number of years in date range from which data is fetched
      pois_keys        : a list of OSM keys @see get_pois_around_point
      features         : a list of feature specifications @see add_feature_from_pois

  The `design` parameter is a function taking a GeoDataFrame and returning a 2D `numpy` array
  where there is a column for each feature and each row is the feature values for a specific
  data item.

  :param printSummary - enable statsmodels summary printing
  :param plotAx       - a matplotlib axis that will be used to plot the fit if specified

  :param data - an INTEGER specifying the year
  """

  # add some missing keys
  build_dataset_kwargs['date'] = date # year of date
  build_dataset_kwargs['property_type'] = property_type

  # create dataset with columns for all the features
  dataset_gdf, pois, gdf_for_num_houses = build_prices_coordinates_features_dataset(
      latitude, longitude, logging=True, **build_dataset_kwargs)

  if dataset_gdf is None:
    print(f"No UK properties"
     + f"were found within a {build_dataset_kwargs['bb_size_km']/2} km radius of ({latitude}, {longitude})")
    return None, None

  if len(dataset_gdf) < 100:
    print(f"Only {len(dataset_gdf)} entires were found.")
    print("No accruate price predictions can be made with fewer than 100 entries.")
    print("Try again with a larger bounding box for location or year")
    return None, None

  print(f"Size of dataset is {len(dataset_gdf)}. Example rows:")
  print(dataset_gdf.drop_duplicates(subset=['postcode']).head().to_string()[0:100000])

  # split
  training_data, testing_data = train_test_split(dataset_gdf, test_size=0.2)

  # create the linear model
  basis = sm.GLM(training_data['price'], design(training_data), family=sm.families.NegativeBinomial())
  results = basis.fit()

  # run predictions on test set to calculate various metrics
  predicts = results.predict(design(testing_data))
  mae = skm.mean_absolute_error(testing_data['price'].to_numpy(), predicts)
  mse = np.sqrt(skm.mean_squared_error(testing_data['price'].to_numpy(), predicts))
  mape = skm.mean_absolute_percentage_error(testing_data['price'].to_numpy(), predicts)

  if printSummary:
    print(results.summary())
    print("=" * 100)
    print(f"Average housing price in training data: {int(np.mean(training_data['price']))} ± {int(np.std(training_data['price']))}σ")
    print(f"Average predicted house price: {int(np.mean(predicts))} ± {int(np.std(predicts))}σ")
    print(f"MAE for price predictions on testing data set is: {int(mae)}")
    print(f"sqrt(MSE) for price predictions on testing data set is: {int(mse)}")
    print(f"Mean absolute percentage error for price predictions on testing data set is: {mape:.4}")
    print("=" * 100)

  if plotAx is not None:
    plotAx.scatter(testing_data['price'].to_numpy(), predicts)
    xs = np.linspace(0,max(testing_data['price'].to_numpy()), 100)
    plotAx.plot(xs, xs, alpha=0.4, color='green')
    plotAx.set_xlabel("True price")
    plotAx.set_ylabel("Predicted price")
    plotAx.set_title(f"True vs. predicted price on held-out training data (n={len(predicts)})")

  # Now run the prediction on the provided data row

  # create the gdf with a single row
  predict_df = pd.DataFrame(columns=['db_id', 'date_of_transfer', 'postcode', 'property_type', 'longitude', 'latitude']).set_index('db_id')
  predict_df.loc[0] = [f'{date}-01-01', 'PLACEHOLDER POSTCODE', property_type, longitude, latitude]
  predict_gdf = assess.make_geodataframe(predict_df)
  # add the features
  for f in build_dataset_kwargs['features']:
    predict_gdf = add_many_features_from_pois(predict_gdf, pois, gdf_for_num_houses, **f)

  print("Feature values:")
  f_names = flatten([f['name'] for f in build_dataset_kwargs['features']])
  print(f_names)
  print(predict_gdf[flatten([f['name'] for f in build_dataset_kwargs['features']])].to_markdown())
  print("Feature values after applying design matrix:")
  print(design(predict_gdf))

  pred_val = results.predict(design(predict_gdf))[0]

  return pred_val, pred_val * mape

def predict_price_simple(latitude, longitude, year, property_type):
  """
  The date must be an integer specifying the year
  """
  pois_keys = {
      "amenity": ["restaurant", "fast_food"],
      "shop": ["supermarket", "convenience"],
      "public_transport": ["platform", "stop_position"],
  }

  features = [
    {
      'func' : 'count',
      'pois_cond' : lambda pois : pois[(pois['amenity'] == 'restaurant') | (pois['amenity'] == 'fast_food')],
      'dist' : 1, # km
      'name' : 'num_restaurants_nearby'
    },
    {
      'func' : 'closest',
      'pois_cond' : lambda pois : pois[(pois['amenity'] == 'restaurant') | (pois['amenity'] == 'fast_food')],
      'dist' : 3, # km
      'name' : 'closest_restaurant'
    },
    {
      'func' : 'closest',
      'pois_cond' : lambda pois : pois[pois['public_transport'].notna()],
      'dist' : 3, # km
      'name' : 'closest_transport'
    },
    {
      'func': 'num_houses',
      'dist': 1, # km
      'name' : 'num_houses_sold_nearby'
    }
  ]

  build_dataset_kwargs = {
      'bb_size_km' : 5,
      # must be 6 km greater to cover distance queries for
      #  houses on the border of the bounding box
      'pois_bb_size_km' : 5 + 6,
      'year_range_size' : 4,
      'pois_keys' : pois_keys,
      'features' : features
  }

  c_param = np.log(.1) / -2

  # design matrix
  def design(gdf : gpd.GeoDataFrame):
    return np.concatenate((
        # just gets the length
        np.ones(gdf['postcode'].shape[0]).reshape(-1,1),
        (gdf['num_restaurants_nearby'].to_numpy()).reshape(-1,1),
        np.exp(-c_param * gdf['closest_restaurant'].to_numpy()).reshape(-1,1),
        np.exp(-c_param * gdf['closest_transport'].to_numpy()).reshape(-1,1),
        (1 / (1 + gdf['num_houses_sold_nearby'].to_numpy() / 100)).reshape(-1,1)
      ), axis=1)

  fig, ax = plt.subplots(figsize=(10,10))
  price_prediction, err = predict_price(latitude, longitude, year, property_type,
    build_dataset_kwargs, design, printSummary=True, plotAx=ax)
  if price_prediction is not None:
    print(f"Predicted price:\n\t\t{price_prediction} ± {err}")
    plt.show()
  else:
    print("Unable to get a price prediction. Set the debug messages for more information.")
    plt.close()

"""
###################################### Evaluations ###########################
"""

def do_pca(design_matrix_fun, data_gdf, features, ncomponents=4):
  """
  Returns a dataframe describing how much variance each principal component
  describes and indicating which features has the highest absolute coefficient
  in the principal component's linear combination.
  """

  feature_names = list(access.flatten([f['name'] for f in features]))

  X = design_matrix_fun(data_gdf, features)

  # normalize by column, such that variance is 1 and mean is 0
  X = (X - X.mean(axis=0)) / X.std(axis=0)

  # We now do PCA

  pca = PCA(n_components=ncomponents)
  pca.fit(X)

  # Transform
  transformed_pca = pca.transform(X)

  # Extract principal components

  num_pcs = pca.components_.shape[0]

  most_important_id = [np.abs(pca.components_[i]).argmax() for i in range(num_pcs)]
  most_important_feature_names = [feature_names[most_important_id[i]] for i in range(num_pcs)]

  df = pd.DataFrame(columns=["principal_component", "most_important_features", "explained_variance"])

  cumulative_var_explained =0
  # Print the most important feature to each PC
  for i, fname in enumerate(most_important_feature_names):
    cumulative_var_explained += pca.explained_variance_ratio_[i]
    df.loc[len(df.index)] = [f"PC{i+1}", fname, pca.explained_variance_ratio_[i]]

  print("=" * 30)
  print(f"These {ncomponents} principal components explains {100*cumulative_var_explained:.4}% of the variance")
  print("=" * 30)

  return df

# vim: set shiftwidth=2 :


import sys
from collections import deque

n = int(sys.stdin.readline().rstrip())

stack = deque()

def push(x):
    stack.append(x)

def size():
    return len(stack)

def empty():
    if size()==0:
        return 1
    else:
        return 0

def top():
    if empty()==1:
        return -1
    else:
        return stack[-1]

def pop():
    if empty()==1:
        return -1
    else:
        return stack.pop()

for _ in range(n):
    line = sys.stdin.readline().rstrip()
    if line.split()[0] == "push":
        push(line.split()[1])

    elif line == "size":
        print(size())

    elif line == "empty":
        print(empty())

    elif line == "top":
        print(top())

    elif line == "pop":
        print(pop())

    else:
        exit()


'''
Задача Иосифа Флавия
n человек, пронумерованных числами от 1 до n, стоят в кругу. Они начинают считаться,
каждый k-й по счету человек выбывает из круга, после чего счет продолжается со
следующего за ним человека. Программа, определяет номер человека, который останется
в кругу последним.
'''

n = int(input('Введите количество человек: '))
k = int(input('Введите номер для счёта: '))
lst = [i for i in range(1, n + 1)]
i = 0
while len(lst) > 1:
    i = (i + k - 1) % len(lst)
    del lst[i]
print('Последним остался человек под номером: ', *lst)


from typing import Union, Optional, Any, Dict, List

from graphql import GraphQLDocument, GraphQLSchema
from graphql.backend.core import GraphQLCoreBackend
from graphql.language.ast import (
    Document,
    FragmentDefinition,
    OperationDefinition,
    Node,
    FragmentSpread,
    Field,
    InlineFragment,
)

class DepthLimitReached(Exception):
    pass

def get_fragments(definitions) -> Dict[str, FragmentDefinition]:
    return {
        definition.name.value: definition
        for definition in definitions
        if isinstance(definition, FragmentDefinition)
    }

def get_queries_and_mutations(definitions) -> List[OperationDefinition]:
    return [
        definition
        for definition in definitions
        if isinstance(definition, OperationDefinition)
    ]

def measure_depth(node: Node, fragments: Dict[str, FragmentDefinition]) -> int:
    """
    A function which recursively measures the depth of a Graphene Query

    :type node: Node
    :param node: Graphql-core object used for query traversal/indexing

    :type fragments: dict
    :param fragments: The fragments of the query

    :rtype: int
    :return: The max depth of the node
    """

    if isinstance(node, FragmentSpread):
        fragment = fragments.get(node.name.value)
        return measure_depth(node=fragment, fragments=fragments)

    elif isinstance(node, Field):
        if node.name.value.lower() in ["__schema", "__introspection"]:
            return 0

        if not node.selection_set:
            return 1

        depths = []
        for selection in node.selection_set.selections:
            depth = measure_depth(node=selection, fragments=fragments)
            depths.append(depth)
        return 1 + max(depths)

    elif (
        isinstance(node, FragmentDefinition)
        or isinstance(node, OperationDefinition)
        or isinstance(node, InlineFragment)
    ):
        depths = []
        for selection in node.selection_set.selections:
            depth = measure_depth(node=selection, fragments=fragments)
            depths.append(depth)
        return max(depths)
    else:
        raise Exception("Unknown node")

def check_max_depth(max_depth: int, document: Document):
    fragments = get_fragments(document.definitions)
    queries = get_queries_and_mutations(document.definitions)

    for query in queries:
        depth = measure_depth(query, fragments)
        if depth > max_depth:
            raise DepthLimitReached(
                "Query is too deep - its depth is {} but the max depth is {}".format(
                    depth, max_depth
                )
            )

class DepthAnalysisBackend(GraphQLCoreBackend):
    def __init__(self, max_depth: int, executor: Optional[Any] = None):
        super().__init__(executor=executor)
        self.max_depth = max_depth

    def document_from_string(
        self, schema: GraphQLSchema, document_string: Union[Document, str]
    ) -> GraphQLDocument:
        document = super().document_from_string(schema, document_string)

        check_max_depth(max_depth=self.max_depth, document=document.document_ast)

        return document


import os
import sys
sys.path.append('..\AOC')
from Utils.utils import timer
import numpy as np
from functools import reduce

rel_path = "input.txt"
script_dir = os.path.dirname(__file__)
abs_file_path = os.path.join(script_dir, rel_path)

input_data = []
with open(abs_file_path, 'r') as f:
    for line in f:
        input_data.append(int(line.rstrip()))

part1 = lambda: reduce(lambda acc, i: acc + int(input_data[i] > input_data[i - 1]), range(1, len(input_data)), 0)
# lambda data: sum(data[i] > data[i-1] for i in range(len(data)))

part2 = lambda nums: reduce(lambda acc, i: acc + int(nums[i] > nums[i - 3]), range(3, len(nums)), 0)
# lambda data, window_size=3: part1(np.convolve(data,np.ones(window_size,dtype=int),'valid'))

# https://adventofcode.com/2021/day/1
if __name__ == "__main__":
    print(part1())
    print(part2(input_data))


import pytest
from UnionFind import UnionFind

#Union-Find is 1-indexed
t1 = ([x for x in range(1,7)], 3, 3)
t2 = ([x for x in range(1,7)], 0, 0)

test_cases = [t1, pytest.param(*t2, marks=pytest.mark.xfail)]

@pytest.mark.parametrize('nodes, x, expected', test_cases)
def test_find(nodes, x, expected):
    clusters = UnionFind(nodes)
    root = clusters.find(x)

    assert(root == expected)

t1 = ([x for x in range(1, 7)], 1, 4, {1:1, 2:2, 3:3, 4:1, 5:5, 6:6})
t2 = ([x for x in range(1, 7)], 4, 1, {1:4, 2:2, 3:3, 4:4, 5:5, 6:6})
t3 = ([x for x in range(1, 7)], 1, 1, {1:1, 2:2, 3:3, 4:4, 5:5, 6:6})

test_cases = [t1, t2, t3]

@pytest.mark.parametrize('nodes, p, q, expected', test_cases)
def test_union(nodes, p, q, expected):
    clusters = UnionFind(nodes)
    clusters.union(p, q)

    new_parents = clusters.parent

    assert(new_parents == expected)

t1 = ('xyz', "[['x', 'y'], ['z']]")

test_cases = [t1]

@pytest.mark.parametrize('nodes, expected', test_cases)
def test_print(nodes, expected):
    clusters = UnionFind(nodes)
    clusters.union('x', 'y')
    string = clusters.__str__()

    assert(string == expected)

t1 = ('xyz', 2)

test_cases = [t1]

@pytest.mark.parametrize('nodes, expected', test_cases)
def test_len(nodes, expected):
    clusters = UnionFind(nodes)
    clusters.union('x', 'y')
    length = clusters.__len__()

    assert(length == expected)


import os
import pickle

import numpy as np
import pandas as pd
from tqdm import tqdm

from common.utils import read_sas7bdata_pd

sides = [None, 'R', 'L']

def build_single_img_based_multi_progression_meta(oai_src_dir, only_baseline=False, use_sas=False):
    # visits =     ['00', '12', '24', '36', '72', '96']
    # exam_codes = ['00', '01', '03', '05', '08', '10']

    visits = ['00', '12', '24', '36', '48', '72', '96']
    exam_codes = ['00', '01', '03', '05', '06', '08', '10']

    # visits =     ['48', '72', '96']
    # exam_codes = ['06', '08', '10']

    non_progressor_code = len(visits)

    grading_types = ['KL', 'OSTL', 'OSTM', 'OSFL', 'OSFM', 'JSL', 'JSM']
    max_grades = {'KL': 4, 'OSTL': 3, 'OSTM': 3, 'OSFL': 3, 'OSFM': 3, 'JSL': 3, 'JSM': 3}
    include_TKR = True
    grad_files = []
    if not os.path.isfile("oai_master_df.csv") or not os.path.isfile("oai_grad_files.pkl"):

        for i, visit in enumerate(visits):
            print(f'==> Reading OAI {visit} visit')

            if use_sas:
                meta = read_sas7bdata_pd(os.path.join(oai_src_dir,
                                                      'Semi-Quant Scoring_SAS',
                                                      f'kxr_sq_bu{exam_codes[i]}.sas7bdat'))
            #     data_clinical = read_sas7bdata_pd(os.path.join(oai_src_dir, f'allclinical{exam_codes[i]}.sas7bdat'))
            else:
                meta = pd.read_csv(os.path.join(oai_src_dir,
                                                'Semi-Quant Scoring_ASCII',
                                                f'kxr_sq_bu{exam_codes[i]}.txt'), sep='|')
            #     # data_clinical = pd.read_csv(os.path.join(oai_src_dir, 'AllClinical_ASCII', f'AllClinical{exam_codes[i]}.txt'), sep='|')
            data_clinical = build_clinical(oai_src_dir, visit_id=exam_codes[i], use_sas=True)

            meta['ID'] = meta['ID'].astype(str)
            meta.replace({'SIDE': {1: sides[1], 2: sides[2]}}, inplace=True)
            data_clinical['ID'] = data_clinical['ID'].astype(str)
            data_clinical.rename(columns={'Side': 'SIDE'}, inplace=True)

            meta = meta.merge(data_clinical, on=['ID', 'SIDE'], how='left')
            # Dropping the data from multiple projects
            meta.drop_duplicates(subset=['ID', 'SIDE'], inplace=True)
            meta.fillna(-1, inplace=True)
            for c in meta.columns:
                meta[c.upper()] = meta[c]
            # Removing the TKR and KL4 at the baseline
            if i == 0:
                meta = meta[meta[f'V{exam_codes[i]}XRKL'] != -1]
                # meta = meta[meta[f'V{exam_codes[i]}XRKL'] < 4]
            # meta = meta[meta[f'V{exam_codes[i]}XRKL'] <= max_grades['KL']]

            meta['KL'] = meta[f'V{exam_codes[i]}XRKL']
            meta['OSTL'] = meta[f'V{exam_codes[i]}XROSTL']
            meta['OSTM'] = meta[f'V{exam_codes[i]}XROSTM']

            meta['OSFL'] = meta[f'V{exam_codes[i]}XROSFL']
            meta['OSFM'] = meta[f'V{exam_codes[i]}XROSFM']

            meta['JSL'] = meta[f'V{exam_codes[i]}XRJSL']
            meta['JSM'] = meta[f'V{exam_codes[i]}XRJSM']

            # Merge levels 0 into 1 in every grading
            # Let TKR be the highest level
            for grading in grading_types:
                meta[grading] = meta[grading].round(decimals=0)
                if grading == "KL":
                    # Remove invalid records that are out of upper bound
                    # meta = meta[meta[grading] <= max_grades[grading]]
                    meta.loc[meta[grading] > max_grades[grading], grading] = None

                    # Set TKR the highest level
                    if include_TKR:
                        meta.loc[meta[grading] == -1, grading] = max_grades[grading] + 1
                        max_kl = max_grades[grading] + 1
                    else:
                        max_kl = max_grades[grading]

                    # Remove invalid records that are out of lower bound
                    # meta = meta[meta[grading] >= 0]
                    meta.loc[meta[grading] < 0, grading] = None

                    for v in range(max_kl):
                        meta = meta.replace({grading: {v + 1: v}})
                else:
                    meta.loc[meta[grading] < 0, grading] = None

            if i == 0:
                _pain_key = f'P01'
            else:
                _pain_key = f'V{exam_codes[i]}'

            # Add visit columns
            meta['visit'] = int(visit)
            meta['visit_id'] = int(exam_codes[i])

            grad_files.append(
                meta[['ID', 'SIDE', 'KL', 'visit', 'visit_id', 'OSTL', 'OSTM', 'OSFL', 'OSFM', 'JSL', 'JSM',
                      'AGE', 'SEX', 'BMI', 'INJ', 'SURG', 'WOMAC', 'V00SITE']])

        id_set_last_fu = set(grad_files[-1].ID.values.astype(int).tolist())  # Subjects present at all FU

        master_df = pd.concat(grad_files)

        with open("oai_grad_files.pkl", "wb") as f:
            pickle.dump(grad_files, f, protocol=4)
        master_df.to_csv("oai_master_df.csv", index=None)
    else:
        print(f'Loading oai_grad_files.pkl')
        with open("oai_grad_files.pkl", "rb") as f:
            grad_files = pickle.load(f)
        master_df = pd.read_csv("oai_master_df.csv")

    master_df['ID'] = master_df['ID'].astype(str)
    # Get baseline and follow-ups
    # for follow_up_id in range(0, len(KL_files)):
    #     KL_files[follow_up_id] = KL_files[follow_up_id].set_index(['ID', 'SIDE'])

    # master_df = master_df.set_index(['ID', 'SIDE'])

    # looking for progressors
    identified_prog = set()

    fus_df = []

    n_bs_knees = grad_files[0].shape[0]

    for bs_knee_id, knee in tqdm(grad_files[0].iterrows(), total=n_bs_knees, desc='Processing OAI:'):
        if int(knee.ID) in identified_prog:
            if identified_prog[int(knee.ID)] == sides[int(knee.SIDE)]:
                continue

        participant = master_df[(master_df['ID'] == knee.ID) & (master_df['SIDE'] == knee.SIDE)]
        participant.sort_values(by=['visit_id'])

        assert len(participant) > 0

        n_follow_ups = len(participant.index)

        for fu1_id in range(n_follow_ups - 1):
            valid_gradings1 = get_valid_gradings_1st_fu(participant.iloc[fu1_id], grading_types, max_grades)

            fu1 = participant.iloc[fu1_id]  # .add_suffix('1')

            # Condition if forced to use baseline only
            if only_baseline:
                fu1_cond = fu1['visit'] == 0
            else:
                fu1_cond = True

            if len(valid_gradings1) == 0:
                print(f'No valid gradings in FU1.')
                continue

            if not fu1_cond:
                print(f'Skip as the initial data is not a baseline.')
                continue

            start_visit = fu1['visit']

            fus = fu1

            valids = []
            for grade_id, grading in enumerate(grading_types):
                prev_grade = fu1[f'{grading}']

                reach_max = False
                valid_by_grading = True
                fus_by_grading = None
                for fu2_id in range(fu1_id + 1, n_follow_ups):
                    fu2 = participant.iloc[fu2_id].add_suffix('2')
                    dt12_y = int((fu2['visit2'] - start_visit) // 12)

                    if not check_valid_grading(fu2[f'{grading}2']):
                        continue

                    # Time points after a time point with max grade must be graded the max value
                    if reach_max or prev_grade == max_grades[grading]:
                        reach_max = True
                        fu2[f'{grading}2'] = max_grades[grading]

                    valid_bl = check_progression_validity(fu1[f'{grading}'], fu2[f'{grading}2'])
                    valid_prev = check_progression_validity(prev_grade, fu2[f'{grading}2'])
                    if not valid_bl or not valid_prev:
                        print(
                            f'[Invalid] {grading}, BL: {fu1[grading]}, prev: {prev_grade}, cur: {fu2[f"{grading}2"]}.')
                        valid_by_grading = False
                        break
                    if prev_grade == 1.0 and fu2[f"{grading}2"] == 0.0:
                        print(f'{grading}, BL: {fu1[grading]}, prev: {prev_grade}, cur: {fu2[f"{grading}2"]}.')

                    new_cols = {}
                    new_cols[f'DT_{dt12_y}y'] = (fu2['visit2'] - start_visit) / 12.0
                    new_cols[f'{grading}_{dt12_y}y'] = fu2[f'{grading}2']
                    new_cols = pd.Series(new_cols)

                    if fus_by_grading is None:
                        fus_by_grading = new_cols
                    else:
                        fus_by_grading = pd.concat([fus_by_grading, new_cols])

                    if check_valid_grading(fu2[f'{grading}2']):
                        prev_grade = fu2[f'{grading}2']

                if not valid_by_grading:
                    fus_by_grading = None

                # End fu2 loop
                if fus_by_grading is not None:
                    fus = pd.concat([fus, fus_by_grading])
                valids.append(valid_by_grading)

            # End grading loop
            if any(valids):
                fus_df.append(fus.to_dict())

    print(f'\nConverting to dataframes...')
    fus_df = pd.DataFrame(fus_df)

    # Remove redundant columns
    # Rename ID and Side
    fus_df = fus_df.rename(columns={'SIDE': 'Side'})
    if 'IDs' in fus_df:
        fus_df = fus_df.drop(columns=['ID2'])
    if 'SIDE2' in fus_df:
        fus_df = fus_df.drop(columns=['SIDE2'])

    fus_df = fus_df.astype({'ID': str})

    return fus_df

def check_valid_grading(v):
    return v is not None and not np.isnan(v)

def build_clinical(oai_src_dir, use_sas=False, visit_id='00'):
    if use_sas:
        data_enrollees = read_sas7bdata_pd(os.path.join(oai_src_dir, 'AllClinical_SAS', 'enrollees.sas7bdat'))
        data_clinical = read_sas7bdata_pd(
            os.path.join(oai_src_dir, 'AllClinical_SAS', f'allclinical{visit_id}.sas7bdat'))
    else:
        data_enrollees = pd.read_csv(os.path.join(oai_src_dir, 'General_ASCII', 'Enrollees.txt'), sep='|')
        data_clinical = pd.read_csv(os.path.join(oai_src_dir, 'AllClinical_ASCII', f'AllClinical{visit_id}.txt'),
                                    sep='|')

    clinical_data_oai = data_clinical.merge(data_enrollees, on='ID')

    clinical_data_oai = clinical_data_oai.replace({'0: No': 0, '1: Yes': 1})

    if visit_id == '00':
        AGE_col = 'V00AGE'
        BMI_col = 'P01BMI'
        HEIGHT_col = 'P01HEIGHT'
        WEIGHT_col = 'P01WEIGHT'
        INJL_col = 'P01INJL'
        INJR_col = 'P01INJR'
        SURGL_col = 'P01KSURGL'
        SURGR_col = 'P01KSURGR'
        WOMACL_col = 'V00WOMTSL'
        WOMACR_col = 'V00WOMTSR'
    else:
        AGE_col = f'V{visit_id}AGE'
        BMI_col = f'V{visit_id}BMI'
        HEIGHT_col = f'V{visit_id}HEIGHT'
        WEIGHT_col = f'V{visit_id}WEIGHT'
        INJL_col = f'V{visit_id}INJL12'
        INJR_col = f'V{visit_id}INJR12'
        SURGL_col = f'V{visit_id}KSRGL12'
        SURGR_col = f'V{visit_id}KSRGR12'
        WOMACL_col = f'V{visit_id}WOMTSL'
        WOMACR_col = f'V{visit_id}WOMTSR'

    # Age, Sex, BMI
    clinical_data_oai['SEX'] = clinical_data_oai['P02SEX']
    clinical_data_oai['AGE'] = clinical_data_oai[AGE_col]
    clinical_data_oai['BMI'] = clinical_data_oai[BMI_col]

    # clinical_data_oai['HEIGHT'] = clinical_data_oai[HEIGHT_col]
    # clinical_data_oai['WEIGHT'] = clinical_data_oai[WEIGHT_col]

    clinical_data_oai_left = clinical_data_oai.copy()
    clinical_data_oai_right = clinical_data_oai.copy()

    # Making side-wise metadata
    clinical_data_oai_left['Side'] = 'L'
    clinical_data_oai_right['Side'] = 'R'

    # Injury (ever had)
    clinical_data_oai_left['INJ'] = clinical_data_oai_left[INJL_col]
    clinical_data_oai_right['INJ'] = clinical_data_oai_right[INJR_col]

    # Surgery (ever had)
    clinical_data_oai_left['SURG'] = clinical_data_oai_left[SURGL_col]
    clinical_data_oai_right['SURG'] = clinical_data_oai_right[SURGR_col]

    # Total WOMAC score
    clinical_data_oai_left['WOMAC'] = clinical_data_oai_left[WOMACL_col]
    clinical_data_oai_right['WOMAC'] = clinical_data_oai_right[WOMACR_col]

    clinical_data_oai_left['V00SITE'] = clinical_data_oai['V00SITE']
    clinical_data_oai_right['V00SITE'] = clinical_data_oai['V00SITE']

    clinical_data_oai = pd.concat((clinical_data_oai_left, clinical_data_oai_right))
    clinical_data_oai.ID = clinical_data_oai.ID.values.astype(str)

    for col in ['BMI', 'INJ', 'SURG', 'WOMAC']:
        clinical_data_oai.loc[clinical_data_oai[col].isin(['.: Missing Form/Incomplete Workbook'])] = None
        clinical_data_oai.loc[clinical_data_oai[col] < 0, col] = None
    return clinical_data_oai[['ID', 'Side', 'AGE', 'SEX', 'BMI', 'INJ', 'SURG', 'WOMAC', 'V00SITE']]

def get_valid_gradings_1st_fu(r, grading_types, max_grades):
    valid_gradings = []
    for grading in grading_types:
        if r[grading] is None or np.isnan(r[grading]) or 0 <= r[grading] < max_grades[grading]:
            valid_gradings.append(grading)
        else:
            # print(f'Init input violation: grading {grading} = {r[grading]}.')
            pass
    return valid_gradings

def check_progression_validity(g1, g2):
    if g1 is None or g2 is None or np.isnan(g1) or np.isnan(g2):
        valid = True
    elif g1 <= g2:
        valid = True
    else:
        valid = False
    return valid


from django.shortcuts import render, get_object_or_404
from fscohort_api.models import Student
from rest_framework.response import Response
from rest_framework.decorators import api_view
from .serializers import StudentSerializer
from rest_framework import status
from fscohort_api.models import Student, Room
from .serializers import StudentSerializer, RoomSerializer

@api_view(["GET","POST"])
def room_list_crate_api(request):

    if request.method == "GET":
        rooms = Room.objects.all()
        serializer = RoomSerializer(rooms, many=True,context={'request': request}) # birden fazla obje döndügü için. yoksa hata verir.
        return Response(serializer.data)

    elif request.method == "POST":
        serializer = RoomSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            data = {
                "message": "Room created successfully"
            }
            return Response(data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(["GET","POST"])
def student_list_crate_api(request):

    if request.method == "GET":
        students = Student.objects.all()
        serializer = StudentSerializer(students, many=True) # birden fazla obje döndügü için. yoksa hata verir.
        return Response(serializer.data)

    elif request.method == "POST":
        serializer = StudentSerializer(data=request.data)
        if serializer.is_valid():
            serializer.save()
            data = {
                "message": "Student created successfully"
            }
            return Response(data, status=status.HTTP_201_CREATED)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

@api_view(["GET", "PUT", "DELETE"])
def student_get_update_delete(request, id):
    student = get_object_or_404(Student, id=id)
    if request.method == "GET" :
        serializer = StudentSerializer(student)
        return Response(serializer.data)
    if request.method == "PUT":
        serializer = StudentSerializer(student, data=request.data)
        if serializer.is_valid():
            serializer.save()
            data = {
                "message": "Student updated successfully"
            }
            return Response(data)
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    if request.method == "DELETE":
        student.delete()
        return Response(status=status.HTTP_204_NO_CONTENT)


from scipy.spatial import Delaunay
import numpy as np
from numpy import linalg as la
import cProfile

def cdiff(array):
	return np.roll(array, -1, axis=0) - array

def dir_derivs(simplex, mesh, values):
	s_points  = mesh.points[simplex] # Points on the simplex
	s_values  = values[simplex] # Values at those points
	delta_r   = la.norm(cdiff(s_points), axis=1) # Norms of vectors between points
	delta_f   = cdiff(s_values) # Difference in the values between points
	return delta_f, delta_r

def smallest_length(points):
	mesh = Delaunay(points)
	delta_rs = np.zeros((len(mesh.simplices), len(mesh.simplices[0])))
	for i, simplex in enumerate(mesh.simplices):
		delta_rs[i] = la.norm(cdiff(mesh.points[simplex]), axis=1)
	return np.min(delta_rs)

def average_length(points):
	mesh = Delaunay(points)
	delta_rs = np.zeros((len(mesh.simplices), len(mesh.simplices[0])))
	for i, simplex in enumerate(mesh.simplices):
		delta_rs[i] = la.norm(cdiff(mesh.points[simplex]), axis=1)
	return np.mean(delta_rs)

def pv_from_dat(data):
	dimensions = len(data[0])-1
	points = data[:,0:dimensions]
	values = data[:,dimensions:]
	return points, values

def filter_threshold(delta_f, threshold_value):
	""" Return indices of  the data whose values are above the acceptable level """
	return delta_f >= threshold_value

def filter_grand(delta_rs, delta_fs, threshold = "one_sigma", criterion = "integral",
				resolution = 0, noise_level = 0):
	""" Filter points base on a combination of filters """

	# Filter resolution
	filter_res = filter_threshold(delta_rs, resolution)
	# Filter noise
	filter_noise = filter_threshold(delta_fs, noise_level)

	if criterion == "integral":
		metric = delta_fs*delta_rs
	elif criterion == "integralsq":
		metric = delta_fs*delta_rs*delta_rs
	elif criterion == "difference":
		metric = delta_fs
	elif criterion == "spikes":
		integrals = delta_fs*delta_rs
		spread = np.std(integrals, axis=1)
		metric = np.outer(spread, np.ones(len(integrals[0])))
	else:
		raise ValueError("Invalid criterion specified. Must be one of 'integral', 'integralsq', 'difference', 'spikes'.")

	# Filter criterion
	if threshold == "mean":
		filter_thres = filter_threshold(metric, np.mean(metric))
	elif threshold == "half":
		filter_thres = filter_threshold(metric, 0.5*metric.max())
	elif threshold == "one_sigma":
		filter_thres = filter_threshold(metric, np.mean(metric)+np.std(metric))
	elif threshold == "two_sigma":
		filter_thres = filter_threshold(metric, np.mean(metric)+2*np.std(metric))
	elif threshold == "non_zero":
		filter_thres = abs(metric) > 1e-10
	else:
		raise ValueError("Invalid threshold specified. Must be one of 'mean', 'half', 'one_sigma', 'two_sigma', 'non_zero")

	return filter_thres*filter_noise*filter_res

def well_scaled_delaunay_mesh(points):
	scales  = []
	offsets = []
	points  = np.array(points)
	for i in range(points.shape[1]):
		offsets.append(points[:,i].min())
		scales.append(1.0/(points[:,i].max()-points[:,i].min()))
		points[:,i] -= offsets[-1]
		points[:,i] *= scales[-1]

	mesh = Delaunay(points)
	return mesh, scales, offsets

def refine_1D(points, values, all_points=False,
			  criterion="integral", threshold="one_sigma",
			  resolution=0, noise_level=0):

	# Do not assume our data is sorted
	sort_inds = np.argsort(points)
	rs_sorted = points[sort_inds]
	fs_sorted = values[sort_inds]
	delta_rs  = np.diff(rs_sorted)
	delta_fs  = np.diff(fs_sorted)

	do_refine = filter_grand(delta_rs, delta_fs, threshold = threshold, criterion = criterion,
							resolution=resolution, noise_level=noise_level)

	new_points = rs_sorted[:-1] + 0.5*delta_rs
	new_points = new_points[np.where(do_refine)]

	if len(new_points) > 0:
		# print("{} new points added.".format(len(new_points)))
		if all_points:
			return np.append(points, new_points)
		return new_points
	else:
		return None

def refine_scalar_field(points, values, all_points=False,
						criterion="integral", threshold="one_sigma",
						resolution=0, noise_level=0):

	mesh, scales, offsets = well_scaled_delaunay_mesh(points)

	values = np.array(values)
	new_points = []
	delta_fs = np.zeros((len(mesh.simplices), len(mesh.simplices[0])))
	delta_rs = np.zeros((len(mesh.simplices), len(mesh.simplices[0])))

	for i, simplex in enumerate(mesh.simplices):
		delta_f, delta_r  = dir_derivs(simplex, mesh, values)
		delta_fs[i] = np.abs(delta_f)
		delta_rs[i] = np.abs(delta_r)

	deltas = filter_grand(delta_rs, delta_fs, threshold = threshold, criterion = criterion,
							resolution=resolution, noise_level=noise_level)

	for i, (d, simp) in enumerate(zip(deltas, mesh.simplices)):
		half_delta_r = 0.5*cdiff(mesh.points[simp])
		for d_i, simp_i, halfr_i in zip(d, simp, half_delta_r):
			if d_i:
				new_points.append(mesh.points[simp_i] + halfr_i)

	if len(new_points) > 0:
		a = np.array(new_points)
		b = np.ascontiguousarray(a).view(np.dtype((np.void, a.dtype.itemsize * a.shape[1])))
		_, idx = np.unique(b, return_index=True)
		unique_a = a[idx]
		# print("{} new points added.".format(len(unique_a)))
		if all_points:
			return np.append(points, unique_a, axis=0)
		for i, (sf, of) in enumerate(zip(scales,offsets)):
			unique_a[:,i] = unique_a[:,i]/sf + of
		return unique_a
	else:
		return None


st = str(input('Enter: ')).replace(' ', '')

# ERRORS
alla = ['+', '-', '*', '/', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0']

def error_han(lin):
    i = 0
    n = 0
    while i < len(lin):
        if lin[i] != '+' and lin[i] != '-' and lin[i] != '*' and lin[i] != '/' and lin[i] != '0' and lin[i] != '1' and lin[i] != '2' and lin[i] != '3' and lin[i] != '4' and lin[i] != '5' and lin[i] != '6' and lin[i] != '7' and lin[i] != '8' and lin[i] != '9':
            print('Error! Given string contains character/characters that are not one of these:')
            print(alla)
            quit()
        elif (lin[i] == '+' or lin[i] == '-' or lin[i] == '*' or lin[i] == '/') and (lin[i + 1] == '+' or lin[i + 1] == '-' or lin[i + 1] == '*' or lin[i + 1] == '/'):
            print("Error! Given string have two or more operators that don't have number between them!")
            quit()
        elif lin[0] == '+' or lin[0] == '-' or lin[0] == '*' or lin[0] == '/':
            print("Error! Given string doesn't begins with number")
            quit()
        elif lin[len(lin) - 1] == '+' or lin[len(lin) - 1] == '-' or lin[len(lin) - 1] == '*' or lin[len(lin) - 1] == '/':
            print("Error! Given string doesn't ends with number")
            quit()
        elif lin[0] == '0' and i != len(lin) - 1 and (lin[1] != '+' and lin[1] != '-' and lin[1] != '*' and lin[1] != '/'):
            print("Error! Given string contains number/numbers that stars with '0'")
            quit()
        elif lin[i] == '+' or lin[i] == '-' or lin[i] == '*' or lin[i] == '/':
            n += 1
        i = i + 1
    if n == 0:
        print("Error! Given string doesn't contains any operators")
        quit()

error_han(st)

# MAKE LIST
# Makes list
l = 0
arr = []
while l < len(st):
    arr.append(st[l])
    l = l + 1

# Marges string numbers
o = 0;
j = []
while o < len(arr):
    sw = ''
    b = 1
    if (arr[o] != '+' and arr[o] != '-' and arr[o] != '*' and arr[o] != '/') and (arr[o - 1] == '+' or arr[o - 1] == '-' or arr[o - 1] == '*' or arr[o - 1] == '/' ):#or (arr[o] is arr[0])
        sw += arr[o]

        while o + b < len(arr) and (arr[o + b - 1] != '+' and arr[o + b - 1] != '-' and arr[o + b - 1] != '*' and arr[o + b - 1] != '/') and (arr[o + b] != '+' and arr[o + b] != '-' and arr[o + b] != '*' and arr[o + b] != '/'):
            sw += arr[o + b]
            b = b + 1
        j.append(sw)
    elif arr[o] == '+' or arr[o] == '-' or arr[o] == '*' or arr[o] == '/':
        sw += arr[o]
        j.append(sw)

    o = o + 1

# Add first number
hu = []
hi = 0

while hi < len(st):
    if st[hi] == '+' or st[hi] == '-' or st[hi] == '*' or st[hi] == '/':
        co = st[:st.index(st[hi])]
        hu.append(co)
        hi = len(st) - 1
    hi = hi + 1

j.insert(0, hu[0])

# CALCULATION
t = 0

# Turn number strings to numbers
ba = 0
while ba < len(j):
    if j[ba] != "+" and j[ba] != "-" and j[ba] != "*" and j[ba] != "/":
        j[ba] = int(j[ba])
    ba = ba + 1

# Devide and multiply
while t < len(j):
    if j[t] == '*':
        j[t - 1] = j[t - 1] * j[t + 1]
        j.pop(t + 1)
        j.pop(t)
        t = 0
    elif j[t] == '/':
        j[t - 1] = j[t - 1] / j[t + 1]
        j.pop(t + 1)
        j.pop(t)
        t = 0
    t = t + 1

# Add and subtract
p = 0
while p < len(j):
    if j[p] == '+':
        j[p - 1] = j[p - 1] + j[p + 1]
        j.pop(p + 1)
        j.pop(p)
        p = 0
    elif j[p] == '-':
        j[p - 1] = j[p - 1] - j[p + 1]
        j.pop(p + 1)
        j.pop(p)
        p = 0
    p = p + 1

print('Answer:', j[0])