import time,os,sys, pygame, cv2
import numpy as np
import tensorflow as tf
from pygame.locals import *
from tensorflow.keras import Sequential, layers
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.layers import Dense
from collections import deque
from tensorflow.keras.models import load_model
pygame.init()

class DQN:
    def __init__(self, size = 5000, inputnumbers = 4, outputnumbers = 4):
        self.inputnumbers = inputnumbers
        self.outputnumbers = outputnumbers
        self.learning_rate = 0.001
        self.momentum = 0.95
        self.eps_min = 0.1
        self.eps_max = 1.0
        self.eps_decay_steps = 2000000
        self.replay_memory_size = size #number of experiences stored in replay memory
        self.replay_memory = deque([], maxlen=size)
        n_steps = 4000000  # total number of training steps
        self.training_start = 10000  # start training after 10,000 game iterations
        self.training_interval = 4  # run a training step every 4 game iterations
        self.save_steps = 1000  # save the model every 1,000 training steps
        self.copy_steps = 10000  # copy online DQN to target DQN every 10,000 training steps
        self.discount_rate = 0.99
        # Skip the start of every game (it's just waiting time).
        self.skip_start = 90
        self.batch_size = 100
        self.iteration = 0  # game iterations
        self.done = True  # env needs to be reset
        self.recordReward = 0

        self.model = self.DQNmodel()

        return

    def stats(self):
        '''This method keep statistics on total rewards obtained during an episode'''
        memory = list(self.replay_memory)
        sizeOfMemory = len(memory)
        self.recordReward += memory[-1][2]

        # print('Total memory: ', str(sizeOfMemory), ' , total rewards: ', str(self.recordReward))
        return 'Total memory: ', str(sizeOfMemory), ' , total rewards: ', str(self.recordReward)

    def DQNmodel(self):
        model = Sequential()
        model.add(Dense(64, input_shape=(self.inputnumbers,), activation='relu'))
        model.add(Dense(64, activation='relu'))
        model.add(Dense(self.outputnumbers, activation='softmax'))
        model.compile(loss='categorical_crossentropy',
                      optimizer=Adam(lr=self.learning_rate))
        return model

    def sample_memories(self, batch_size):
        '''This method defines how memories/experiences are sampled from the replay memory
        before being passed as parameters to the DQN'''
        indices = np.random.permutation(len(self.replay_memory))[:batch_size]
        # state, action, reward, next_state, continue
        cols = [[], [], [], [], []]
        for idx in indices:
            memory = self.replay_memory[idx]
            for col, value in zip(cols, memory):
                col.append(value)
        cols = [np.array(col) for col in cols]
        return (cols[0], cols[1], cols[2].reshape(-1, 1), cols[3].reshape(-1,1), cols[4].reshape(-1, 1))

    def epsilon_greedy(self, q_values, step):
        '''This method defines the exploration policy for the agent based on the epsilon
        greedy algorithm'''
        self.epsilon = max(self.eps_min, self.eps_max -
                           (self.eps_max-self.eps_min) * step/self.eps_decay_steps)
        if np.random.rand() < self.epsilon:
            return np.random.randint(4)  # random action
        else:
            return np.argmax(q_values)  # optimal action

class RoboObstacle:
    def __init__(self, fps=50, storageSize = 5000, possibilities = 4, windowsize = (300,300), xlimit = 10, ylimit = 10, env_learning_rate=0.3):
        # set up the window
        self.windowsize = windowsize
        self.DISPLAYSURF = pygame.display.set_mode(self.windowsize, 0, 32)
        pygame.display.set_caption('RoboObstacle')
        # set up the colors
        self.BLACK = (0, 0, 0)
        self.WHITE = (255, 255, 255)
        self.RED = (255, 0, 0)
        self.GREEN = (0, 255, 0)
        self.BLUE = (0, 0, 255)
        #set number of possible grids on x and y axis
        self.xlimit = xlimit
        self.ylimit = ylimit

        self.dict_shapes = {} #dictionary holding position of obstacles per row in the window or environment
        self.shapes_state = []

        self.Agent = DQN(size = storageSize, inputnumbers = possibilities, outputnumbers = possibilities+1)
        pygame.init()
        self.FPS = fps
        self.fpsClock = pygame.time.Clock()

        self.possibilities = possibilities
        self.updateBinariesKey = {}
        self.movement = []
        self.dimension_x = int(self.windowsize[0]/xlimit) #width of each grid
        self.dimension_y = int(self.windowsize[1]/ylimit) #height of each grid
        self.startLocation = (0, 0)
        self.endLocation = (0, 0)
        self.Endkey = ''
        self.DecisionTracker = {}
        self.currentDecision = []

        #set factor for number of obstacles
        self.env_learning_rate = env_learning_rate

        #directory to image of destination robot
        self.RoboDir = './Images/dest.jpg'
        #set parameter for static robot
        self.static = False
        #end program
        self.end = False

    def trainDQN(self):
        X_state_val, X_action_val, rewards, X_next_state_val, continues = (
            self.Agent.sample_memories(self.Agent.batch_size))

        next_q_values = self.Agent.model.predict(X_state_val)
        max_next_q_values = np.max(
            next_q_values, axis=1, keepdims=True)
        y_val = rewards + continues * self.Agent.discount_rate * max_next_q_values

        # Train the online DQN
        # print('fitting: ', len(X_state_val), ' datas')
        self.Agent.model.fit(X_state_val, tf.keras.utils.to_categorical(
            X_next_state_val, num_classes=self.Agent.outputnumbers), verbose=0)
        return True

    def show_n_shapes(self , generate_shapes = True):
        '''This method defines the part of the environment that has obstacles. It also enables retaining the obstacle
        positions within the environment when generate_shapes is set to False'''
        # display Robot
        # RobotImage = pygame.image.load(self.RoboDir)
        # RobotImage = pygame.transform.scale(RobotImage, (30, 30))
        # self.DISPLAYSURF.blit(RobotImage, self.endLocation)

        if generate_shapes == True:
            for i in range(self.ylimit):
                x_poses = [f for f in range(self.xlimit)] #list of all possible obstacle positions on the x-axis
                # number_of_obstacles = np.random.choice(x_poses)
                number_of_obstacles = int(self.xlimit * self.env_learning_rate) #number of obstacles per row
                y_location = (i * self.windowsize[1])/self.ylimit
                #location of obstacles
                self.shapes_state = []
                #iterate over each row and randomly place (and colour) m number of obstacles and store all selected grid positions in self.dict_shapes
                for m in range(number_of_obstacles):
                    x_position = np.random.choice(x_poses, replace = False)
                    self.shapes_state.append(x_position)
                    x_location = x_position * self.dimension_x
                    color = tuple([np.random.choice([rgb for rgb in range(255)]) for i in range(3)])

                    self.dict_shapes[str(int(x_location))+'_'+str(int(y_location))] = (color, x_location, y_location)

                    pygame.draw.rect(self.DISPLAYSURF, color, (x_location, y_location, self.dimension_x, self.dimension_y))

        #retain the environment structure when generate_shapes is set to False
        else:
            for i, j in self.dict_shapes.items():
                pygame.draw.rect(self.DISPLAYSURF, j[0], (j[1], j[2], self.dimension_x, self.dimension_y))
        return

    def display(self):
        pygame.display.update()
        self.fpsClock.tick(self.FPS)

        for event in pygame.event.get():

            if event.type == QUIT:
                # self.AgentA.model.save('models/AgentA.h5')
                pygame.quit()
                sys.exit()
        return

    def displayObstacles(self, generate_shapes = True):
        if generate_shapes == True:
            # ylimit = 10,  xlimit = 10, generate_shapes = True
            self.show_n_shapes(generate_shapes = generate_shapes)
        else:
            self.show_n_shapes(generate_shapes = generate_shapes)

        return True

    def evaluate(self, state):
        obs, reward, done, info = '', False, True, 'failure'
        if state in self.shapes_state:
            reward = False
        else:
            reward = True
        return obs, reward, done, info

    def DrawRobot(self, location_x, location_y):
        box_dimension_x, box_dimension_y = int(self.windowsize[0]/self.xlimit), int(self.windowsize[1]/self.ylimit)

        self.robot = pygame.image.load('Images/robot.png')
        self.robot = pygame.transform.scale(self.robot, (box_dimension_x, box_dimension_y))

        self.DISPLAYSURF.blit(self.robot, (int(location_x), int(location_y)))

        return

    def step(self, state):
        # state is the interpretation of the neural network next coordinate (x_y coordinate location)
        # Clear previous displays
        location_x, location_y = state.split('_')

        # decide state of robot
        # State varies from 0 to 9
        # display the source robot
        self.DrawRobot(location_x, location_y)

        # display the destination robot
        xdest, ydest = self.endLocation

        self.DrawRobot(xdest, ydest)
        self.display()
        # time.sleep()

        obs, reward, done, info = self.evaluate(state)
        return obs, reward, done, info

    def binarize(self, current_coordinate):
        '''This method converts movements to coordinates and updates the values of self.updateBinariesKey keys.
        It also makes sure the agent never goes out of the window frame. It checks if the chosen movement is an obstacle
        by comparing with self.dict_shapes, a dictionary that stores obstacle positions'''
        # current_coordinate must follow this format, x_y.
        # where x is the x coordinate
        # where y is the y coordinate
        # coordinate = str(x) + '_'+ str(y)

        # degree of movement (left, right, front, back, stay_in_position)

        # 10 locations, modify from index, 4 - 10
        # 0 - left
        # 1 - right
        # 2 - Front
        # 3 - Back

        x, y = tuple(current_coordinate.split('_'))
        x, y = int(x), int(y)
        # print(x, y)

        if y < 0:
            y = self.dimension_y

        if y > self.windowsize[1]:
            y =self.windowsize[1] - self.dimension_y

        if x < 0:
            x = self.dimension_x

        if x > self.windowsize[0]:
            x =self.windowsize[0] - self.dimension_x

        self.updateBinariesKey[0] = str(x) + '_'+ str(y - self.dimension_y)
        self.updateBinariesKey[1] = str(x) + '_'+ str(y + self.dimension_y)
        self.updateBinariesKey[2] = str(x - self.dimension_x) + '_'+ str(y)
        self.updateBinariesKey[3] = str(x + self.dimension_x) + '_'+ str(y)
        self.updateBinariesKey[4] = str(x) + '_'+ str(y) # remain in position

        front_movement = (str(x) + '_'+ str(y - self.dimension_y)) in self.dict_shapes.keys()
        back_movement = (str(x) + '_'+ str(y + self.dimension_y)) in self.dict_shapes.keys()
        left_movement = (str(x - self.dimension_x) + '_'+ str(y)) in self.dict_shapes.keys()
        right_movement = (str(x + self.dimension_x) + '_'+ str(y)) in self.dict_shapes.keys()

        if self.Euclidean_distance(current_coordinate, self.Endkey) == 0:
            print('Arrived destination')
            time.sleep(5)
            self.end = True

        if (front_movement == False) and (back_movement == False) and (left_movement== False) and (right_movement == False):
            self.static = True
            # self.end = True

        else:
            self.static = False

        binary = np.array([front_movement, back_movement, left_movement, right_movement]).reshape(-1, 1)
        return binary

    def interpret(self, prediction):
        # interpret the result: x_y
        value = np.argmax(prediction)
        output = self.updateBinariesKey[value]
        return output

    def DecideCoordinate(self, excludingPoint = None):
        '''This method randomly selects the start and end coordinates of the agent
        '''
        x, y = 0, 0
        m= int(self.windowsize[0]/self.dimension_x)
        choice = np.random.choice([i for i in range(m)])
        for i in range(choice, m):
            for j in range(int(self.ylimit)):
                key = str(int(i*self.dimension_x)) + '_' + str(int(j*self.dimension_y))
                if key in self.dict_shapes.keys() or key == excludingPoint:
                    pass
                else:
                    x = int(i*self.dimension_x)
                    y = int(j*self.dimension_y)
                    break

            break
        return str(x) + '_' + str(y), (x, y) #key, coordinate

    def TrackNextMove(self, move):
        self.movement.append(move)
        output = False
        counts = np.unique(self.movement, return_counts = True)
        if counts[0][0] == self.updateBinariesKey[4] and counts[1][0] > 2000:
            output = True
        else:
            output = False
        return output

    def Euclidean_distance(self, source, destination):
        x1, y1 = tuple(source.split('_'))
        x1, y1 = int(x1), int(y1)
        x2, y2 = tuple(destination.split('_'))
        x2, y2 = int(x2), int(y2)
        distance = np.sqrt(pow(y2-y1, 2)+ pow(x2 - x1, 2))
        return distance

    def sortMovement(self, dictData):
        vals = list(dictData.values())
        keys = list(dictData.keys())
        vals.sort()
        sortedKeys = []
        for i in vals:
            for j in keys:
                if dictData[j] == i:
                    sortedKeys.append(j)

        return sortedKeys

    def DefineAction(self, key):
        x, y = tuple(key.split('_'))
        x, y = int(x), int(y)

        front_movement = (str(x) + '_'+ str(y - self.dimension_y)) in self.dict_shapes.keys()
        back_movement = (str(x) + '_'+ str(y + self.dimension_y)) in self.dict_shapes.keys()
        left_movement = (str(x - self.dimension_x) + '_'+ str(y)) in self.dict_shapes.keys()
        right_movement = (str(x + self.dimension_x) + '_'+ str(y)) in self.dict_shapes.keys()

        OutputDict = {}
        for i, BooleanStatus in enumerate([front_movement, back_movement, left_movement, right_movement]):
            if BooleanStatus == False:
                # calcuate Euclidean Distance
                 distance = self.Euclidean_distance(self.updateBinariesKey[i], self.Endkey)
                 OutputDict[i] = distance
            else:
                pass

        interpretPosition = {0: 'front', 1: 'back', 2: 'left', 3: 'right', 4: 'stay'}

        # Decide to go through the path with the shortest distance
        # print(OutputDict)
        decision = self.sortMovement(OutputDict)
        self.currentDecision = decision
        # print(decision[0], interpretPosition[decision[0]])
        return decision[0], interpretPosition[decision[0]]

def TrainNetwork(iterations = 5000, model_name = 'RoboNET'):
    robo = RoboObstacle(storageSize = iterations)
    robo.DISPLAYSURF.fill(robo.WHITE)

    #show robot on image
    # frame = cv2.imread('./Images/dest.jpg')
    # cv2.imshow('image', frame)
    # cv2.waitKey(0)
    cv2.destroyAllWindows()

    robo.displayObstacles(generate_shapes = True)
    Endkey, EndCoordinate = robo.DecideCoordinate() # Ending Coordinate
    robo.endLocation = EndCoordinate
    robo.Endkey = Endkey

    i = 0
    state = 0
    iteration = 0
    iterations = 2000000
    successes = 0

    startKey, startCoordinate = robo.DecideCoordinate(excludingPoint = Endkey) # starting Coordinate
    robo.startLocation = startCoordinate
    key = startKey
    print('=============================================================================')
    print('Start Coordinate: ', startCoordinate, ' End Coordinate: ', EndCoordinate)
    print('=============================================================================')
    print('boundaries coordinates')
    print('=============================================================================')
    print(robo.dict_shapes)
    print('=============================================================================')
    while (not robo.end) and (iteration < iterations):
        # Fill window with white grids
        robo.DISPLAYSURF.fill(robo.WHITE)
        # Display Generated Obstacles
        robo.displayObstacles(generate_shapes = False)
        # Make move
        obs, reward, done, info = robo.step(key)

        # binarize Obstacles
        binaries = np.array(robo.binarize(key)).reshape(1, -1)
        predictions = robo.Agent.model.predict(binaries)

        # action = np.random.choice([i for i in range(5)])
        action, interpretAction = robo.DefineAction(key)
        # key = robo.interpret(predictions)
        key = robo.updateBinariesKey[action]

        # Track next move, if next move is static for 2000 iterations break
        robo.TrackNextMove(key)
        # action = robo.Agent.epsilon_greedy(q_value, iteration)
        successes+= reward

        # Let's memorize what just happened
        baseline_value = robo.Agent.replay_memory_size
        # if successes > int(baseline_value/2) and iteration == len(list(robo.Agent.replay_memory)):
        if successes == int(baseline_value) and reward == True:
            robo.Agent.model.save('./models/{}.h5'.format(model_name))
            break
        elif successes < int(baseline_value * 0.8) and reward == True:
            # replace random false with new inputs
            robo.Agent.replay_memory.append((np.array(binaries[0]), action, reward, action, 1.0 - done))

        else:
            pass

        # print('binaries: ', binaries, ' predictions: ', predictions, ' action: ', action, ' Key: ', key, ' Data Size: ', robo.Agent.stats())
        # robo.Agent.stats()

        # Train Network
        robo.trainDQN()
        i+= 1

        if i == 10:
            i = 0
        else:
            pass

        iteration += 1

        if 0xFF == ord('q'):
            break

        # display update
        robo.display()

    lengthOfQueue = len(list(robo.Agent.replay_memory))
    return successes, iteration, lengthOfQueue

if __name__ == "__main__":
    TrainNetwork()


#%% Imports
import matplotlib
import matplotlib.pyplot as plt
import numpy as np
import scipy.constants as constants
from uncertainties import *
import os
from scipy.optimize import curve_fit
# pregunta 5
#%% Funciones
def ec_de_la_clase(tau):    return (0.7104-0.1331*np.exp(-3.449*tau))

def T_T_eff(log_tau):   return [(((3/4)*(10**i+ec_de_la_clase(10**i)))**(1/4))
                                for i in log_tau]

def q(tau, T):  return [((4./3.)*((T[i]/T_eff_sol)**4)-(tau[i]))
                        for i in range(len(tau))]

def ec_de_la_clase2(tau):   return [(0.7104-0.1331*np.exp(-3.449*i))
                                    for i in tau]

# Constantes
sigma = 5.67*10**-5                 #Constate de Stefan-Boltzman en cgs
C = constants.speed_of_light * 100  #velocidad de la luz en cm/s
T_eff_sol = 5778 #kelvin

#%% Datos
archivo = open('data.txt','r')
data = archivo.readlines()
tau_0, T, log_p_g, log_p_e, log_k_p, x = ([] for i in range(6))
for i in range(len(data)):
    data[i] = data[i].split(' ')
    data[i][len(data[i])-1] = data[i][len(data[i])-1].strip('\n')
    tau_0.append(float(data[i][0]))
    T.append(float(data[i][1]))
    log_p_g.append(float(data[i][2]))
    log_p_e.append(float(data[i][3]))
    log_k_p.append(float(data[i][4]))
    x.append(float(data[i][5])*(10**5)) #En [cm]
tau = [10**i for i in tau_0] #quitandole el log
P_rad, k_0 = [], []
for i in range(len(log_p_e)):
    p_e = 10**log_p_e[i]
    k0_pe = 10**log_k_p[i]
    k_0.append(k0_pe * p_e)
    P_rad.append(np.log10(4*sigma*(T[i]**4)/(3*C)))
#%% Parte a
# Plots
fig, ((ax1, ax2), (ax3, ax4), (ax5, ax6), (ax7, ax8), (ax9, ax10)) = plt.subplots(5, 2, sharex='col',
                                                                                sharey='row',figsize=(10, 8))
opciones = {'markersize':4, 'marker':'+', 'markeredgecolor':'black'}
ax1.plot(tau_0, T, 'r--', **opciones)
ax2.plot(x, T, 'r--', **opciones)
ax3.plot(tau_0, P_rad, 'y--', **opciones)
ax4.plot(x, P_rad, 'y--', **opciones)
ax5.plot(tau_0, log_p_g, 'b--', **opciones)
ax6.plot(x, log_p_g, 'b--', **opciones)
ax7.plot(tau_0, log_p_e, 'g--', **opciones)
ax8.plot(x, log_p_e, 'g--', **opciones)
ax9.plot(tau_0, k_0, 'm--', **opciones)
ax10.plot(x, k_0, 'm--', **opciones)
# Ajustes de los ejes
#ticks
ax1.set_yticks(np.arange(3000, 10000, 1000))
ax3.set_yticks(np.arange(0, 1.5, .2))
ax5.set_yticks(np.arange(3, 8, .5))
ax7.set_yticks(np.arange(-2, 5, 1))
ax9.set_yticks(np.arange(0, 15, 2))
#labels
opciones_de_labels = {'fontsize':14, 'rotation':0,
                    'horizontalalignment':'right',
                    'verticalalignment':'center'}
ax1.set_ylabel(r'$T^{\circ}$', **opciones_de_labels)
ax3.set_ylabel(r'$log(P_{rad})$', **opciones_de_labels)
ax5.set_ylabel(r'$log(P_{g})$', **opciones_de_labels)
ax7.set_ylabel(r'$log(P_{e})$', **opciones_de_labels)
ax9.set_ylabel(r'$\kappa_{0}$', **opciones_de_labels)
ax9.set_xlabel(r'$log(\tau_{0})$', fontsize=16)
ax10.set_xlabel('Altura geometrica [cm]', fontsize=16)
#guardado
plt.savefig('grafico_p5_01.pdf')
plt.show()
#%% Parte 2
xp = np.linspace(-4,1,100)
plt.plot(tau_0, [(i/T_eff_sol) for i in T],'r--', markersize=6, marker='+',
        markeredgecolor='black', label='Datos de la Tabla')
plt.plot(xp, T_T_eff(xp),'b--', label='Ecuacion mostrada en clases')
plt.xlabel(r'$log(\tau)$', fontsize=16)
plt.ylabel(r'$T\ /\ T_{eff}$', fontsize=16)
plt.legend()
plt.savefig('grafico_p5_02.pdf')
plt.show()
#%% otro grafico de la parte b
xp = np.linspace(-1,15,100)
plt.plot(xp, ec_de_la_clase2(xp),'b--', label='Ec. de hopf')
plt.plot(tau, q(tau,T), 'r--', marker = '+',
        markeredgecolor='black', markersize=7, label='Datos a partir de la tabla')
plt.ylabel(r'$q(\tau)$', fontsize=16)
plt.xlabel(r'$\tau$', fontsize=16)
plt.legend(loc=2)
plt.xlim(-.5,2.6)
plt.ylim(0.3,1.4)
plt.subplots_adjust(left=0.12, right=0.95, top=0.9, bottom=0.2)
plt.savefig('grafico_p5_04.pdf')
plt.show()
#%% Parte c
for i in range(len(tau_0)):
    if tau[i]+q(tau,T)[i]>=4/3:
            Teff_igual_T = i
            print('tau + q(tau): {:.3f}'.format(tau[i]+q(tau,T)[i]))
            print('indice de la lista: {}'.format(i))
            print('tau correspondiente: {:.3f}'.format(tau[i]))
            print('log(tau) correspondiente: {:.3f}'.format(tau_0[i]))
            break


# Reading an excel file using Python
import xlrd

from py2neo import Graph

graph=Graph('bolt://localhost:7687',auth=("neo4j","MyGraphP@ss"))

def getEmail(s):
    email=""
    i=3
    while i<len(s)-1:
        email=email+s[i]
        i=i+1
    return email

def getBusinessRole(s):
    role=""
    i=3
    while i<len(s)-1:
        role=role+s[i]
        i=i+1
    return role

ActorRole={}
BusinessRole=[]

def getData(indexRow,indexCol):
    if(sheet.cell_value(indexRow,indexCol)=='[]'):
        return
    array=sheet.cell_value(indexRow, indexCol).split(')')
    firstItem=0
    #[('steve.olinde@enron.com', 25, ['specialist', 'trading'])
    for item in array:
        if item ==']':  #last item
            break;
        email=""
        role=""
        oneActor=item.split(",")
        if firstItem==0:
            #[('steve.olinde@enron.com
            email=getEmail(oneActor[0])
            #['specialist'
            if not oneActor[2] == '[]':
                role=getBusinessRole(oneActor[2])
            firstItem=1
        else:
            email=getEmail(oneActor[1])
            if not oneActor[3] == '[]':
                role=getBusinessRole(oneActor[3])

        ActorRole[email]=role
        if(not role=='' and role not in BusinessRole ):
                    BusinessRole.append(role)

# Give the location of the file
loc = ("C:/Users/user/Desktop/Courses/Master2/Stage/MyPapers/chatbot/Marwa Files/resultm__forney.xlsx")

# To open Workbook
wb = xlrd.open_workbook(loc)
sheet = wb.sheet_by_index(0)

#get emails from actRecipients
i=1
while i<sheet.nrows:
    getData(i,6)
    i=i+1

#get emails from actInCC
i=1
while i<sheet.nrows:
    getData(i,7)
    i=i+1

print(ActorRole)
print(BusinessRole)

#create query to insert data in neo4j
''''
for actor in ActorRole:
    query="MERGE (n: Actor {Email: '"+actor+"'})"
    try:
        data=graph.run(query)
    except:
        print("Error1 ")
        print("actor not created "+actor)

    if not ActorRole[actor]=='':
        query2="MERGE (m: Role {Name: '"+ActorRole[actor]+"'})"
        try:
            data=graph.run(query2)
        except:
            print("Error2 ")

        query3="MATCH (n: Actor{Email: '"+actor+"'}),(m: Role {Name: '"+ActorRole[actor]+"'})" \
                "MERGE (n)-[:ActorWithRole]->(m)"
        try:
            data=graph.run(query3)
        except:
            print("Error3 please repeat the question with another syntax")
'''


'''
Created on Sep 13, 2014

@author: DemiCow
'''

import pygame
import scene
import label
import maphandler
import squid
import prepare

class Cutscene0(scene.Scene):

    def __init__(self):
        scene.Scene.__init__(self)

        self.next = "GAMEPLAY"
        self.init_variables()
        self.init_objects()

    def init_variables(self):
        """ init all variables needed in scene"""
        self.speed = self.MASTER_SPEED
        self.scroll_to_left = True

        self.title_time = self.time
        self.wave_number = 0
        #self.collisiontimer = 0
        #self.shield_counter = 0
        self.shield_time = -100
        self.collision_time = -100
        self.click_counter = 5
        self.text_delay = 100
        self.secret_line = True

    def init_objects(self):
        """ creates objects needed in specfic scenes"""

        self.squid = squid.Squid(self)
        self.squid.rect.y += 325

        self.foreground_map = maphandler.ScrollingMap(self, 'splash')
        self.maps = [self.foreground_map]
        #pygame.mixer.init()
        self.ocean_noises =  pygame.mixer.Sound(prepare.SOUNDS['31762__slanesh__ocean'])
        self.seagull_noises =  pygame.mixer.Sound(prepare.SOUNDS['144836__eelke__sea-seagulls-crows-windfilter'])
        self.click_sound = pygame.mixer.Sound(prepare.SOUNDS['157539__nenadsimic__click'])

        self.tutorial = label.Label(self, size = (800,150), location = (800,150), font_size = 20)
        if self.android:
            self.tutorialtext = [['Welcome to "it has a squid in it".', 'touch to skip'],
                                 ['this squid is going', 'on a journey', 'your task is to guide him'],
                                  ['what will you find', 'at the end?'],
                                  ['tilt to move around', 'eat shrimp for a power-up'],
                                  ['hit those who would stand in your way', 'with ink bullets'],
                                  ['target groups for', 'a combo bonus.'],
                                  ['when you run out of lives, your',' journey is over. good luck!'],
                                  ['"Fate is a sea without shore, and the soul is a rock that abides;',
                                   '"But her ears are vexed with the roar and her face with the foam of the tides."',
                                   "- Swinburne"]
                                  ]

        else:
            self.tutorialtext = [
                                  ['"Fate is a sea without shore,' ],
                                   ['and the soul is a rock that abides;'],
                                   ['But her ears are vexed with the roar '],
                                   ['and her face with the foam of the tides." - Swinburne'],
                                  ]

        self.linecounter = 0

        self.tutorial.textlines = self.tutorialtext[0]
        self.tutorial.clickable = False
        self.tutorial.textcolor = (pygame.color.Color("black"))
        self.tutorial.backgroundcolor = self.tutorial.transparent_color

        self.sprites = [self.foreground_map, self.squid,
                        self.tutorial]

    def startup(self, time, persistant):
        if prepare.MUSIC_ON == True:
            self.ocean_noises.play(loops=-1)
            self.seagull_noises.play(loops=-1)
        self.start_time = time
        return scene.Scene.startup(self, time, persistant)

    def cleanup(self):
        return scene.Scene.cleanup(self)

    def update_specifics(self, time, time_delta):
        self.click_counter -= 1
        self.squid.dx = self.squid.dy = 0
        self.squid.currentimage = self.squid.imgsquidge

        if self.click_counter%2 == 0:
            self.squid.rect.x += 2
            self.squid.rect.y += 1
        else: pass
        if self.clicked ==True:
            if prepare.MUSIC_ON == True:
                self.click_sound.play()
                self.start_time= self.time

        if self.clicked ==True and self.time - self.start_time > 100000*time_delta:#self.click_counter < -self.text_delay:
            self.click_counter = 100
            self.start_time = self.time
            self.linecounter += 1
            if self.linecounter == len(self.tutorialtext):
                if prepare.MUSIC_ON == True:
                    self.ocean_noises.fadeout(1000)
                    self.seagull_noises.fadeout(1000)
                self.done = True
            else:
                self.tutorial.image.fill(self.tutorial.fillcolor)
                self.tutorial.textlines = self.tutorialtext[self.linecounter]

class Cutscene1(scene.Scene):
    def __init__(self):
        scene.Scene.__init__(self)
        self.next = "TITLE"
        self.init_variables()
        self.init_objects()

    def init_objects(self):
        """ creates objects needed in specfic scenes"""
        self.collision_time = -100
        self.shield_time = -100
        self.speed = self.MASTER_SPEED
        self.label = label.Label(self, font_size = 50)
        self.label.textlines = ["THANK YOU FOR PLAYING", "IT HAS A SQUID IN IT"]
        self.menu = label.Label(self, font_size = 50)
        if self.android:
            self.menu.textlines = ["touch to go back to title"]
        else: self.menu.textlines = ["click to go back to title"]
        self.menu.clickable = True
        self.menu.rect.y += 300
        self.background = pygame.Surface(self.screen.get_size())
        self.backgroundrect = self.background.get_rect()
        self.background.fill(pygame.color.Color("black"))
        self.screen.blit(self.background, (0, 0))
        self.sprites = [self.label, self.menu]
        self.click_sound = pygame.mixer.Sound(prepare.SOUNDS['157539__nenadsimic__click'])

    def mouse_controls(self, time_delta):
        pass

    def startup(self, time, persistant):
        self.start_time = time
        return scene.Scene.startup(self, time, persistant)

    def cleanup(self):
        return scene.Scene.cleanup(self)

    def fill_background(self):
        self.screen.blit(self.background, (0, 0))

    def update_specifics(self, time,time_delta):
        """update title screen"""
        if self.clicked ==True:
            if self.menu.option_highlighted == 0:
                if prepare.MUSIC_ON == True:
                    self.click_sound.play()

                self.done = True

def main():
    pygame.init()
    pygame.mixer.init()
    import gameplay
    import title
    run_it = scene.Control()
    state_dict = {"TITLE" : title.Title(),
                  "INTRO" : Cutscene0(),
                  "GAMEPLAY" : gameplay.gamePlay(),
                  "ENDING": Cutscene1()
                   }
    run_it.setup_states(state_dict, "INTRO")
    run_it.main()

if __name__ == "__main__":
    main()


import sqlite3
from unittest import result

# Builds the list of events "db" from the database
def load_db(start_time, end_time):
    sqlite_connection = sqlite3.connect('database.db')
    try:
        sqlite_connection.execute('CREATE TABLE IF NOT EXISTS events (id TEXT PRIMARY KEY ON CONFLICT FAIL, start TEXT, end TEXT, reg_nro TEXT, merkki TEXT, asiakas TEXT, puh_nro TEXT, tyomaarays TEXT, invoice INT NOT NULL CHECK (invoice IN (0, 1)));')
#       sqlite_connection.execute('CREATE TABLE IF NOT EXISTS events (id TEXT PRIMARY KEY ON CONFLICT FAIL, start TEXT, end TEXT, reg_nro TEXT, merkki TEXT, asiakas TEXT, puh_nro TEXT, tyomaarays TEXT, invoice INT NOT NULL CHECK (invoice IN (0, 1)), FOREIGN KEY (id) REFERENCES services (varaus_id) ON DELETE CASCADE);')
#       sqlite_connection.execute('CREATE TABLE IF NOT EXISTS events (id INTEGER PRIMARY KEY ASC, ... # to get automatic id
        sqlite_connection.execute('CREATE TABLE IF NOT EXISTS services (varaus_id TEXT, rivinumero INTEGER, service TEXT, price TEXT);')
#       sqlite_connection.execute('CREATE TABLE IF NOT EXISTS services (varaus_id TEXT, rivinumero INTEGER, service TEXT, price TEXT, FOREIGN KEY (varaus_id) REFERENCES events (id) ON DELETE CASCADE);')
        sqlite_connection.commit()
    except Exception as ex:
        template = "An exception of type {0} occurred. Arguments:\n{1!r}"
        message = template.format(type(ex).__name__, ex.args)
        print(message)

    sqlite_connection.row_factory = sqlite3.Row #without this line, fetchall will return Tuples, not Row objects!
    sqlite_cursor = sqlite_connection.cursor()

    sql_string = "SELECT * FROM events WHERE start BETWEEN ? AND ? ORDER BY start;"
    sqlite_cursor.execute(sql_string, (start_time, end_time))

    db = [] # a list of dictionaries, to enable jsonify(db)
    for row in sqlite_cursor.fetchall():
        # print(type(row)) # it should be sqlite.Row (not Tuple).
        row_dictionary = dict(row)
        db.append(row_dictionary)
    #sqlite_connection.close()  # not needed?
    return db

def append_db(event):
    try:
        sqlite_connection = sqlite3.connect('database.db')
        sqlite_cursor = sqlite_connection.cursor()
        sql_string = "INSERT INTO events VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?);"
        sqlite_cursor.execute(sql_string, (
            event["id"],\
            event["start"],
            event["end"],
            event["reg_nro"],
            event["merkki"],\
            event["asiakas"],\
            event["puh_nro"],\
            event["tyomaarays"],
            event["invoice"] ) )
        sqlite_connection.commit()
        result = "OK"
    except Exception as ex:
        template = "An exception of type {0} occurred. Arguments:\n{1!r}"
        message = template.format(type(ex).__name__, ex.args)
        print(message)
        result = "NOT_OK"
    return result

def get_services(id):
    sqlite_connection = sqlite3.connect('database.db')
    sqlite_connection.row_factory = sqlite3.Row #without this line, fetchall will return Tuples, not Row objects!
    sqlite_cursor = sqlite_connection.cursor()
    sql_string = "SELECT rivinumero, service, price FROM services WHERE varaus_id = ? ORDER BY rivinumero;"
    sqlite_cursor.execute(sql_string, (id,))

    service_dictionary = {} # includes field names (service or price) and their values
    for row in sqlite_cursor.fetchall():
        wanted_row_dictionary = {}
        # here we don't use dict(row)
        row_keys = row.keys()
        row_values = tuple(row)
        row_number = row_values[0] # its type is class 'int', an integer

        # build a dictionary of this row
        wanted_row_dictionary[ row_keys[1] + str(row_number) ] = row_values[1] # service field
        wanted_row_dictionary[ row_keys[2] + str(row_number) ] = row_values[2] # price field
        service_dictionary.update(wanted_row_dictionary)
    #sqlite_connection.close()  # not needed?
    return service_dictionary

    # previous version returned a list of dictionaries without field names
    service_row_list = [] # a list of dictionaries, to enable jsonify()
    for row in sqlite_cursor.fetchall():
        row_dictionary = {}
        row_keys = row.keys()
        row_values = tuple(row)
        value_index = 0
        # build a dictionary of this row
        for key in row_keys:    # one key-value pair at a time
            row_dictionary[key] = row_values[value_index] #could there be a smarter way? Yes, row_dictionary = dict(row)
            value_index += 1
        service_row_list.append(row_dictionary)
    #sqlite_connection.close()  # not needed?
    return service_row_list

#def add_services(varaus_id, line_list):
def add_services(services):
    try:
        sqlite_connection = sqlite3.connect('database.db')
        sqlite_cursor = sqlite_connection.cursor()
        sql_string = "INSERT INTO services VALUES (?, ?, ?, ?);"
        varaus_id = services["id"]

        for line_index in range( len(services) // 2 ):
#            print(services["service"+str(line_index)], services["price"+str(line_index)] )
            sqlite_cursor.execute(sql_string, (varaus_id, line_index, services["service"+str(line_index)], services["price"+str(line_index)]) )

        sql_string = "UPDATE events SET invoice = ? WHERE id = ?"
        sqlite_cursor.execute(sql_string, ( 1, varaus_id ))

#    Old version:
#       line_index = 0
#       for row_dictionary in line_list:
#           line_index += 1
#           sqlite_cursor.execute(sql_string, (varaus_id, line_index, row_dictionary["service"], row_dictionary["price"]) )

        sqlite_connection.commit()
        result = "OK"
    except Exception as ex:
        template = "An exception of type {0} occurred. Arguments:\n{1!r}"
        message = template.format(type(ex).__name__, ex.args)
        print(message)
        result = "NOT_OK"
    return result

def move_db(event):
    sql_string = "UPDATE events SET start = ?, end = ? WHERE id = ?"
    try:
        sqlite_connection = sqlite3.connect('database.db')
        sqlite_cursor = sqlite_connection.cursor()
        sqlite_cursor.execute(sql_string, (
            event["newStart"],
            event["newEnd"],
            event["id"] ))
        sqlite_connection.commit()
        result = "OK"
    except Exception as ex:
        template = "An exception of type {0} occurred. Arguments:\n{1!r}"
        message = template.format(type(ex).__name__, ex.args)
        print(message)
        result = "NOT_OK"
    return result

def update_db(event):
    sql_string = "UPDATE events SET "\
    + "reg_nro = ?, "\
    + "merkki = ?, "\
    + "asiakas = ?, "\
    + "puh_nro = ?, "\
    + "tyomaarays = ? "\
    + "WHERE id = ?"
    try:
        sqlite_connection = sqlite3.connect('database.db')
        sqlite_cursor = sqlite_connection.cursor()
        sqlite_cursor.execute(sql_string, (\
            event["reg_nro"],\
            event["merkki"],\
            event["asiakas"],\
            event["puh_nro"],\
            event["tyomaarays"],\
            event["id"] ))
        sqlite_connection.commit()
        result = "OK"
    except Exception as ex:
        template = "An exception of type {0} occurred. Arguments:\n{1!r}"
        message = template.format(type(ex).__name__, ex.args)
        print(message)
        result = "NOT_OK"
    return result

def delete_db(event_id):
    try:
        sqlite_connection = sqlite3.connect('database.db')
        sqlite_cursor = sqlite_connection.cursor()
        sql_string = "DELETE FROM events WHERE id = ?;"
        sqlite_cursor.execute(sql_string, (event_id,) ) # extra comma needed to define Tuple with only one element

        sql_string = "DELETE FROM services WHERE varaus_id = ?;"
        sqlite_cursor.execute(sql_string, (event_id,) ) # extra comma needed to define Tuple with only one element

        sqlite_connection.commit()
        result = "OK"

#       Test if CASCADE works. The services should have been deleted for this event
        print("The services for the deleted event:", get_services(event_id))

    except Exception as ex:
        template = "An exception of type {0} occurred. Arguments:\n{1!r}"
        message = template.format(type(ex).__name__, ex.args)
        print(message)
        result = "NOT_OK"
    return result


#!/usr/bin/env python
#
# tournament.py -- implementation of a Swiss-system tournament
#

import psycopg2

def connect():
    """Connect to the PostgreSQL database.  Returns a database connection."""
    return psycopg2.connect("dbname=tournament")

def deleteMatches():
    """Remove all the match records from the database."""
    create_tournament()

def deletePlayers():
    """Remove all the player records from the database."""
    query = '''UPDATE players SET deleted = true;'''
    count = run_query(query)

def countPlayers():
    """Returns the number of players currently registered."""
    query = '''SELECT count FROM player_count;'''
    count = run_query(query)
    return count[0][0]

def registerPlayer(name):
    """Adds a player to the tournament database.

    The database assigns a unique serial id number for the player.  (This
    should be handled by your SQL database schema, not in your Python code.)

    Args:
      name: the player's full name (need not be unique).
    """
    tournament_id = get_tournament_id()
    query = '''INSERT INTO players (player_name, tournament_id) VALUES (%s, %s);'''
    run_query(query, (name, tournament_id))
    print(name + " registered.")

def playerStandings():
    """Returns a list of the players and their win records, sorted by wins.

    The first entry in the list should be the player in first place, or a player
    tied for first place if there is currently a tie.

    Returns:
      A list of tuples, each of which contains (id, name, wins, matches):
        id: the player's unique id (assigned by the database)
        name: the player's full name (as registered)
        wins: the number of matches the player has won
        matches: the number of matches the player has played
    """
    query = '''SELECT * FROM standings;'''
    return run_query(query)

def reportMatch(winner, loser):
    """Records the outcome of a single match between two players.

    Args:
      winner:  the id number of the player who won
      loser:  the id number of the player who lost
    """
    tournament_id = get_tournament_id()
    query = '''INSERT INTO matches (tournament_id, first_player_id, second_player_id, winner_id ) VALUES (%s, %s, %s, %s);'''
    run_query(query, (tournament_id, winner, loser, winner))

def swissPairings():
    """Returns a list of pairs of players for the next round of a match.

    Assuming that there are an even number of players registered, each player
    appears exactly once in the pairings.  Each player is paired with another
    player with an equal or nearly-equal win record, that is, a player adjacent
    to him or her in the standings.

    Returns:
      A list of tuples, each of which contains (id1, name1, id2, name2)
        id1: the first player's unique id
        name1: the first player's name
        id2: the second player's unique id
        name2: the second player's name
    """
    standings = playerStandings()
    matches = []
    match = ()
    match_count = 1;
    last_player = ''
    for player in standings:
        last_player = player
        if match_count == 2:
            match = match + (player[0], player[1])
            matches.append(match)
            match_count = 1
        else:
            match = (player[0], player[1])
            match_count += 1

    # if we end with player this player gets a free win to the next round
    '''if match_count == 1:
        reportMatch(standings[:-1][0], 0)'''

    return matches

def run_query(query, query_data = "()"):
    """Records the outcome of a single match between two players.

    Args:
      query:  the query to execute
      data_query:  any tuple params to make sure sql is escaped
    """
    conn = connect()
    c = conn.cursor()
    c.execute(query, query_data)
    return_data = ""

    if query.find("SELECT") < 0:
        conn.commit()
    else:
        return_data = c.fetchall()

    conn.close()
    return return_data

def get_tournament_id():
    """Records the outcome of a single match between two players.

    Return:
      The data from the excuted query
    """
    query = '''SELECT current_tournament_id FROM tournament;'''
    count = run_query(query)
    return count[0][0]

def create_tournament():
    """Creates a new tournament."""
    query = '''INSERT INTO tournaments (tournament_name) VALUES ('The Big thing!');'''
    count = run_query(query)

#Start off a tourment the first time the script is ran
create_tournament()


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

# # importing libraries

# In[1]:

import matplotlib.pyplot as plt
import numpy as np
import cv2 as cv
import math

# # loading image

# In[2]:

img_path = './Bird2.jpg'
rgb_img = plt.imread(img_path)
rgb_img.shape

# # Convert to GrayScale

# In[3]:

grayscale = cv.cvtColor(rgb_img,cv.COLOR_RGB2GRAY)
grayscale.shape

# In[4]:

plt.figure(figsize = (50, 15))
plt.subplot(1, 2, 1)
plt.hist(grayscale.ravel(),256,[0,256]);

values, count = np.unique(grayscale, return_counts=True)
plt.subplot(1, 2, 2)
plt.bar([x for x in range(0,256)], count, width=0.9)

plt.savefig('./Histogram_Output.png')

# In[ ]:


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Wed Mar  2 10:29:55 2022

@author: saraporras
"""

import numpy as np
import matplotlib.pyplot as plt

import scipy.special as spice
import scipy.optimize as opt

from scipy.optimize import curve_fit
from scipy.stats import gmean

plt.rcParams['mathtext.fontset'] = 'stix'
plt.rcParams['font.family'] = 'STIXGeneral'
plt.rc('font', size=20)
plt.rc('axes', titlesize=18)
plt.rc('axes', labelsize=18)
plt.rc('xtick', labelsize=24)
plt.rc('ytick', labelsize=24)
plt.rc('legend', fontsize=24)
plt.rc('figure', titlesize=17)
plt.rc('xtick', top=True, direction='in')
plt.rc('ytick', right=True, direction='in')
plt.rc('xtick.major', size=7, width=1.5, top=True)
plt.rc('ytick.major', size=7, width=1.5, right=True)
plt.rc('xtick.minor', size=4, width=1)
plt.rc('ytick.minor', size=4, width=1)
#
# #        Rmax        Vmax      Radius
# Grand_dmo = np.loadtxt('../../RmaxVmaxRadDMO0_1.txt')
# Grand_hydro = np.loadtxt('../../RmaxVmaxRadFP0_1.txt')

#        Rmax        Vmax      Radius
Grand_dmo = np.loadtxt('../Data_subhalos_simulations/RmaxVmaxRadDMO0_1.txt')
Grand_hydro = np.loadtxt('../Data_subhalos_simulations/RmaxVmaxRadFP0_1.txt')

Grand_hydro = Grand_hydro[Grand_hydro[:, 1] > 1e-4, :]

Grand_dmo = Grand_dmo[Grand_dmo[:, 0] > 2 * 0.184, :]
Grand_hydro = Grand_hydro[Grand_hydro[:, 0] > 2 * 0.184, :]

Grand_dmo[:, 2] *= 1e3
Grand_hydro[:, 2] *= 1e3

Grand_dmo = Grand_dmo[np.argsort(Grand_dmo[:, 1])]
Grand_hydro = Grand_hydro[np.argsort(Grand_hydro[:, 1])]

Grand_dmo_raw = Grand_dmo.copy()
Grand_hydro_raw = Grand_hydro.copy()

# Los mayores Dist_Gc son 264 kpc, pero entonces cuál es R200?
R200 = 263  # kpc

plt.close('all')

# %%  CALCULATE THE RMAX-VMAX RELATION

def Cv_Mol2021(V, x, ci=[1.12e5, -0.9512, -0.5538, -0.3221, -1.7828]):
    # Median subhalo concentration depending on its Vmax and distance to the host center
    # Moline et al. 2110.02097
    #
    # V - max radial velocity of a bound particle in the subhalo [km/s]
    # x - distance from the subh to the halo center [kpc]
    return (ci[0] * (1 + (
        sum([(ci[i + 1] * np.log10(V)) ** (i + 1) for i in range(3)])))
            * (1 + ci[4] * np.log10(x)))

def Cv_Mol2021_redshift0(V, c0=1.75e5, c1=-0.90368, c2=0.2749, c3=-0.028):
    # Median subhalo concentration depending on its Vmax and its redshift (here z=0)
    # Moline et al. 2110.02097
    #
    # V - max radial velocity of a bound particle in the subhalo [km/s]
    ci = [c0, c1, c2, c3]
    return ci[0] * (1 + (sum([ci[i + 1] * np.log10(V) ** (i + 1)
                              for i in range(3)])))

def Cv_Grand(Vmax, mm, bb):
    return 2. * (10 ** (-bb) * Vmax ** (1. - mm) / H0) ** 2

def Cv_Grand_points(xx, yy):
    return 2. * (xx / yy / H0) ** 2

H0 = 70. / 1e3

def calculate_med(dataset, num_interv, perc_low=20, perc_high=80, nmax=60):
    x_approx = np.logspace(np.log10(dataset[0, 1]), np.log10(nmax),
                           num=num_interv)
    # print(x_approx)

    ymed = []
    yp20 = []
    yp80 = []
    # print(dataset[:, 1])

    for i in range(num_interv - 1):
        # print(len(np.where(dataset[:, 1] > x_approx[i])[0]),
        #       len(np.where(dataset[:, 1] > x_approx[i + 1])[0]))
        if len(np.where(dataset[:, 1] > x_approx[i + 1])[0]) == 0 \
                or len(np.where(dataset[:, 1] > x_approx[i])[0]) == 0 \
                or (len(np.where(dataset[:, 1] > x_approx[i])[0])
                    == len(np.where(dataset[:, 1] > x_approx[i + 1])[0])):
            ymed.append(0)
            yp20.append(0)
            yp80.append(0)
        else:
            xmin = np.where(dataset[:, 1] > x_approx[i])[0][0]
            xmax = np.where(dataset[:, 1] > x_approx[i + 1])[0][0]

            # print(dataset[:, 0][xmin:xmax])

            ymed.append(np.median(dataset[:, 0][xmin:xmax]))
            yp20.append(np.nanpercentile(dataset[:, 0][xmin:xmax], perc_low))
            yp80.append(np.nanpercentile(dataset[:, 0][xmin:xmax], perc_high))

    x_approx = (x_approx[:-1] + x_approx[1:]) / 2.
    y_ceros = np.array(ymed) > 0.
    x_approx = x_approx[y_ceros]
    ymed = np.array(ymed)[y_ceros]
    yp20 = np.array(yp20)[y_ceros]
    yp80 = np.array(yp80)[y_ceros]

    return x_approx, ymed, yp20, yp80

def calculate_mean(dataset, num_interv, perc_low=20, perc_high=80, nmax=60):
    x_approx = np.logspace(np.log10(dataset[0, 1]), np.log10(nmax),
                           num=num_interv)
    # x_approx = np.logspace(np.log10(0.95), np.log10(np.min([60, np.max(dataset[:, 1])])), num=num_interv)

    ymed = []

    for i in range(num_interv - 1):
        if len(np.where(dataset[:, 1] > x_approx[i + 1])[0]) == 0 \
                or len(np.where(dataset[:, 1] > x_approx[i])[0]) == 0 \
                or (len(np.where(dataset[:, 1] > x_approx[i])[0])
                    == len(np.where(dataset[:, 1] > x_approx[i + 1])[0])):
            ymed.append(0)
        else:
            xmin = np.where(dataset[:, 1] > x_approx[i])[0][0]
            xmax = np.where(dataset[:, 1] > x_approx[i + 1])[0][0]

            ymed.append(np.mean(dataset[:, 0][xmin:xmax]))

    x_approx = (x_approx[:-1] + x_approx[1:]) / 2.
    y_ceros = np.array(ymed) > 0.
    x_approx = x_approx[y_ceros]
    ymed = np.array(ymed)[y_ceros]
    return x_approx, ymed

def calculate_gmean(dataset, num_interv, nmax=60):
    x_approx = np.logspace(np.log10(dataset[0, 1]), np.log10(nmax),
                           num=num_interv)

    ymed = []
    for i in range(num_interv - 1):
        # print(len(np.where(dataset[:, 1] > x_approx[i])[0]),
        #       len(np.where(dataset[:, 1] > x_approx[i + 1])[0]))
        if len(np.where(dataset[:, 1] > x_approx[i + 1])[0]) == 0 \
                or len(np.where(dataset[:, 1] > x_approx[i])[0]) == 0 \
                or (len(np.where(dataset[:, 1] > x_approx[i])[0])
                    == len(np.where(dataset[:, 1] > x_approx[i + 1])[0])):
            ymed.append(0)
        else:
            xmin = np.where(dataset[:, 1] > x_approx[i])[0][0]
            xmax = np.where(dataset[:, 1] > x_approx[i + 1])[0][0]

            ymed.append(gmean(dataset[:, 0][xmin:xmax]))

    x_approx = (x_approx[:-1] + x_approx[1:]) / 2.
    y_ceros = np.array(ymed) > 0.
    x_approx = x_approx[y_ceros]
    ymed = np.array(ymed)[y_ceros]

    return x_approx, ymed

def fits2(data, xxx, limit):
    Xlimit = np.where(xxx >= limit)[0][0]
    return np.polyfit(np.log10(xxx[Xlimit:]), np.log10(data[Xlimit:]), 1)

def cumulative(sigma, x1, x0, perc):
    return 0.5 * (1 + spice.erf((x1 - x0) / sigma / 2 ** 0.5)) - perc

def mass_from_Vmax(Vmax, Rmax, c200):
    # From Moline16
    return Vmax ** 2 * Rmax * 3.086e16 * 1e9 / 6.6741e-11 / 1.989e30 * ff(
        c200) / ff(2.163)

def def_Cv(c200, Cv):
    return 200 * ff(2.163) / ff(c200) * (c200 / 2.163) ** 3 - Cv

def ff(x):
    return (np.log(1 + x) - 1 / (1 + x))

def power_law_from_median(median, perct20, perct80, xx, limit):
    fitsM, fitsB = fits2(median, xx, limit)
    print('m and b')
    print("%.3f  %.3f" % (fitsM, fitsB))
    print()

    exponents = []
    exponents.append(fitsB)

    yy = perct20 / xx ** fitsM
    Xlimit = np.where(xx >= limit)[0][0]
    bb = np.polyfit(np.log10(xx[Xlimit:]), np.log10(yy[Xlimit:]), 0)
    exponents.append(bb[0])
    print('Low percentile:  b=%.3f' % bb[0])

    yy = perct80 / xx ** fitsM
    Xlimit = np.where(xx >= limit)[0][0]
    bb = np.polyfit(np.log10(xx[Xlimit:]), np.log10(yy[Xlimit:]), 0)
    exponents.append(bb[0])
    print('High percentile: b=%.3f' % bb[0])

    return fitsM, exponents

def sigma_from_2080(exponents):
    bb20 = opt.root(cumulative, 0.25, args=(exponents[0], exponents[1], 0.2))
    print(bb20['x'], 10 ** (bb20['x'][0]))

    bb80 = opt.root(cumulative, 0.1, args=(exponents[0], exponents[2], 0.8))
    print(bb80['x'], 10 ** (bb80['x'][0]))

    # xmax-xmed = n.sigma in a normal distribution
    print('n from sigmas: %.3f and %.3f' % (
        (-exponents[0] + exponents[1]) / bb20['x'],
        (-exponents[2] + exponents[0]) / bb80['x']))

    return [bb20['x'], bb80['x']]

def C200_from_Cv(yy_med, y20, y80, xx):
    median = Cv_Grand_points(xx, yy_med)
    perct20 = Cv_Grand_points(xx, y20)
    perct80 = Cv_Grand_points(xx, y80)

    C200_med = []
    C200_20 = []
    C200_80 = []

    for i in range(len(median)):
        C200_med.append(opt.root(def_Cv, 40, args=median[i])['x'][0])
        C200_20.append(opt.root(def_Cv, 40, args=perct20[i])['x'][0])
        C200_80.append(opt.root(def_Cv, 40, args=perct80[i])['x'][0])

    return np.array(C200_med), np.array(C200_20), np.array(C200_80)

def Cm_Mol16(M, x, ci=[19.9, -0.195, 0.089, 0.089, -0.54]):
    # Median subhalo concentration depending on its mass and distance
    # to the host center
    # Moline et al. 1603.04057
    #
    # M - tidal mass of the subhalo [Msun]
    # x - distance from the subh to the halo center [kpc]
    return ci[0] * (
            1 + (sum([(ci[i + 1] * np.log10(M * 0.7 / 10 ** 8)) ** (i + 1)
                      for i in range(3)]))) * (
                   1 + ci[4] * np.log10(x / R200))

# %% CALCULATE THE MEDIAN VALUES OF THE DISTRIBUTION Rmax-Vmax

number_bins_dmo = 18
number_bins_hydro = 20

# Limit to the power law tendency
hydroLimit = 11
dmoLimit = 11

cv_dmo_cloud = Cv_Grand_points(Grand_dmo_raw[:, 1], Grand_dmo_raw[:, 0])
cv_hydro_cloud = Cv_Grand_points(Grand_hydro_raw[:, 1], Grand_hydro_raw[:, 0])

Grand_dmo[:, 0] = cv_dmo_cloud
Grand_hydro[:, 0] = cv_hydro_cloud

vv_medians_dmo, cv_dmo_median, cv_dmo_min, cv_dmo_max = calculate_med(
    Grand_dmo, number_bins_dmo, perc_low=16, perc_high=84)
vv_medians_hydro, cv_hydro_median, cv_hydro_min, cv_hydro_max = calculate_med(
    Grand_hydro, number_bins_hydro, perc_low=16, perc_high=84)

vv_means_dmo, cv_dmo_mean = calculate_mean(
    Grand_dmo, number_bins_dmo, perc_low=16, perc_high=84)
vv_means_hydro, cv_hydro_mean = calculate_mean(
    Grand_hydro, number_bins_hydro, perc_low=16, perc_high=84)

vv_gmeans_dmo, cv_dmo_gmean = calculate_gmean(
    Grand_dmo, number_bins_dmo)
vv_gmeans_hydro, cv_hydro_gmean = calculate_gmean(
    Grand_hydro, number_bins_hydro)

plt.close('all')

plt.figure()
plt.ylim(1e3, 2e5)

plt.scatter(Grand_dmo[:, 1], cv_dmo_cloud, color='k', s=3, label='DMO')
plt.scatter(Grand_hydro[:, 1], cv_hydro_cloud, color='limegreen',
            label='Hydro', alpha=0.5, s=3)

plt.plot(vv_medians_dmo, cv_dmo_median, '-k')
plt.plot(vv_medians_hydro, cv_hydro_median, '-g')

plt.plot(vv_medians_dmo, cv_dmo_max, '--k')
plt.plot(vv_medians_hydro, cv_hydro_max, '--g')

plt.plot(vv_medians_dmo, cv_dmo_min, '--k')
plt.plot(vv_medians_hydro, cv_hydro_min, '--g')

plt.xscale('log')
plt.yscale('log')
plt.legend()

plt.xlabel(r'$V_\mathrm{max}$ [km s$^{-1}$]', size=28)
plt.ylabel(r'c$_\mathrm{V}$', size=28)

# FIGURE 2
plt.subplots(12, figsize=(14, 7))
ax1 = plt.subplot(121)

plt.plot(vv_medians_dmo, cv_dmo_median, '.k', ms=13, label='median')
plt.plot(vv_medians_hydro, cv_hydro_median, '.g', ms=13)

plt.plot(vv_means_dmo, cv_dmo_mean, 'xk', ms=13, label='mean')
plt.plot(vv_means_hydro, cv_hydro_mean, 'xg', ms=13)

plt.plot(vv_gmeans_dmo, cv_dmo_gmean, '+k', ms=13, label='gmean')
plt.plot(vv_gmeans_hydro, cv_hydro_gmean, '+g', ms=13)

plt.plot(vv_medians_dmo, cv_dmo_max, '--k')
plt.plot(vv_medians_hydro, cv_hydro_max, '--g')

plt.plot(vv_medians_dmo, cv_dmo_min, '--k')
plt.plot(vv_medians_hydro, cv_hydro_min, '--g')

plt.ylabel(r'c$_\mathrm{V}$', size=28)

plt.xscale('log')
plt.yscale('log')
plt.legend()

plt.xlabel(r'$V_\mathrm{max}$ [km s$^{-1}$]', size=28)

plt.subplot(122, sharex=ax1, sharey=ax1)

print('Median--------------')
print('DMO')
fitsM_dmo, exps_dmo = power_law_from_median(cv_dmo_median, cv_dmo_min,
                                            cv_dmo_max, vv_medians_dmo,
                                            dmoLimit)
print('Scatter: %.3f and %.3f' % (
    exps_dmo[0] - exps_dmo[1], exps_dmo[2] - exps_dmo[0]))

print()
print('Hydro')
fitsM_hydro, exps_hydro = power_law_from_median(cv_hydro_median,
                                                cv_hydro_min, cv_hydro_max,
                                                vv_medians_hydro, hydroLimit)
print('Scatter: %.3f and %.3f' % (exps_hydro[0] - exps_hydro[1],
                                  exps_hydro[2] - exps_hydro[0]))
print()
print()

# print('Mean--------------')
# print('DMO')
# fitsM_dmo_mean, exps_dmo_mean = power_law_from_median(
#     cv_dmo_mean, cv_dmo_min, cv_dmo_max, vv_means_dmo, dmoLimit)
# print('Scatter: %.3f and %.3f' % (exps_dmo_mean[0] - exps_dmo_mean[1],
#                                   exps_dmo_mean[2] - exps_dmo_mean[0]))
#
# print()
# print('Hydro')
# fitsM_hydro_mean, exps_hydro_mean = power_law_from_median(
#     cv_hydro_mean, cv_hydro_min, cv_hydro_max, vv_means_hydro, hydroLimit)
# print('Scatter: %.3f and %.3f' % (exps_hydro_mean[0] - exps_hydro_mean[1],
#                                   exps_hydro_mean[2] - exps_hydro_mean[0]))
#
# print()
# print()

print('Gmean--------------')
print('DMO')
fitsM_dmo_gmean, exps_dmo_gmean = power_law_from_median(
    cv_dmo_gmean, cv_dmo_min, cv_dmo_max, vv_gmeans_dmo, dmoLimit)
print('Scatter: %.3f and %.3f' % (exps_dmo_gmean[0] - exps_dmo_gmean[1],
                                  exps_dmo_gmean[2] - exps_dmo_gmean[0]))

print()
print('Hydro')
fitsM_hydro_gmean, exps_hydro_gmean = power_law_from_median(
    cv_hydro_gmean, cv_hydro_min, cv_hydro_max, vv_gmeans_hydro, hydroLimit)
print('Scatter: %.3f and %.3f' % (exps_hydro_gmean[0] - exps_hydro_gmean[1],
                                  exps_hydro_gmean[2] - exps_hydro_gmean[0]))

xx_plot = np.logspace(np.log10(vv_medians_dmo[0]), np.log10(60))

plt.plot(xx_plot, 10 ** exps_dmo[0] * xx_plot ** fitsM_dmo, '-k', linewidth=3,
         label='median')
plt.plot(xx_plot, 10 ** exps_hydro[0] * xx_plot ** fitsM_hydro, '-g',
         linewidth=3)

# plt.plot(xx_plot, 10 ** exps_dmo_mean[0] * xx_plot ** fitsM_dmo_mean, '--k',
#          linewidth=3, label='mean')
# plt.plot(xx_plot, 10 ** exps_hydro_mean[0] * xx_plot ** fitsM_hydro_mean,
#          '--g',
#          linewidth=3)

plt.plot(xx_plot, 10 ** exps_dmo_gmean[0] * xx_plot ** fitsM_dmo_gmean, ':k',
         linewidth=3, label='gmean')
plt.plot(xx_plot, 10 ** exps_hydro_gmean[0] * xx_plot ** fitsM_hydro_gmean,
         ':g',
         linewidth=3)

plt.plot(vv_medians_dmo, cv_dmo_median, '.k', ms=13)
plt.plot(vv_medians_hydro, cv_hydro_median, '.g', ms=13)

# plt.plot(vv_means_dmo, cv_dmo_mean, 'xk', ms=13)
# plt.plot(vv_means_hydro, cv_hydro_mean, 'xg', ms=13)

plt.axvline(dmoLimit, linestyle='-', color='k', alpha=0.3, linewidth=2)
plt.axvline(hydroLimit, linestyle='-', color='g', alpha=0.3, linewidth=2)

plt.plot(xx_plot, Cv_Mol2021_redshift0(xx_plot), color='red',
         label='Moliné+21', alpha=0.5, linewidth=3)

# fit to moline21
def log10MOline21(V, c0, c1, c2, c3):
    return np.log10(Cv_Mol2021_redshift0(V, c0, c1, c2, c3))

moline_fits = curve_fit(log10MOline21,
                        xdata=vv_medians_dmo[vv_medians_dmo > dmoLimit],
                        ydata=np.log10(
                            cv_dmo_median[vv_medians_dmo > dmoLimit]),
                        p0=[1.75e5, -0.90368, 0.2749, -0.028],
                        bounds=([-np.inf, -5, -5, -5],
                                [+np.inf, 5, 5, 5]))

print(moline_fits)

plt.plot(xx_plot, Cv_Mol2021_redshift0(V=xx_plot,
                                       c0=moline_fits[0][0],
                                       c1=moline_fits[0][1],
                                       c2=moline_fits[0][2],
                                       c3=moline_fits[0][3]),
         color='k',
         label='Moliné+21 fit', alpha=0.5, linewidth=3)

moline_fits = curve_fit(log10MOline21,
                        xdata=vv_medians_hydro[vv_medians_hydro > hydroLimit],
                        ydata=np.log10(
                            cv_hydro_median[vv_medians_hydro > hydroLimit]),
                        p0=[1.e4, -0.90368, 0.2749, -0.028],
                        bounds=([-np.inf, -np.inf, -np.inf, -np.inf],
                                [5e5, +np.inf, +np.inf, +np.inf]))

print(moline_fits)

plt.plot(xx_plot, Cv_Mol2021_redshift0(V=xx_plot,
                                       c0=moline_fits[0][0],
                                       c1=moline_fits[0][1],
                                       c2=moline_fits[0][2],
                                       c3=moline_fits[0][3]),
         color='g',
         alpha=0.5, linewidth=3)

plt.xscale('log')
plt.yscale('log')
plt.legend()

plt.xlabel(r'$V_\mathrm{max}$ [km s$^{-1}$]', size=28)

# plt.figure()
#
# xmin = np.where(Grand_dmo_raw[:,1]>=10)[0][0]
# xmax = np.where(Grand_dmo_raw[:,1]>20)[0][0]
#
# median_dmo = np.median(Grand_dmo_raw[xmin:xmax, 0])
# mean_dmo = np.mean(Grand_dmo_raw[xmin:xmax, 0])
# gmean_dmo = gmean(Grand_dmo_raw[xmin:xmax, 0])
#
# plt.hist(Grand_dmo_raw[xmin:xmax, 0], log=True, color='k', alpha=0.5,
#          bins=np.logspace(np.log10(np.min(Grand_dmo_raw[xmin:xmax, 0])),
#                           np.log10(np.max(Grand_dmo_raw[xmin:xmax, 0])), num=15))
#
# plt.axvline(median_dmo, linestyle='-', color='k', label='median')
# plt.axvline(mean_dmo, linestyle='--', color='k', label='mean')
# plt.axvline(gmean_dmo, linestyle=':', color='k', label='gmean')
#
# plt.xscale('log')
# #---------------------
# xmin = np.where(Grand_hydro_raw[:,1]>=10)[0][0]
# xmax = np.where(Grand_hydro_raw[:,1]>20)[0][0]
#
# median_dmo = np.median(Grand_hydro_raw[xmin:xmax, 0])
# mean_dmo = np.mean(Grand_hydro_raw[xmin:xmax, 0])
# gmean_dmo = gmean(Grand_hydro_raw[xmin:xmax, 0])
#
# plt.hist(Grand_hydro_raw[xmin:xmax, 0], log=True, color='limegreen', alpha=0.5,
#          bins=np.logspace(np.log10(np.min(Grand_hydro_raw[xmin:xmax, 0])), np.log10(np.max(Grand_hydro_raw[xmin:xmax, 0])), num=15))
#
# plt.axvline(median_dmo, linestyle='-', color='limegreen')
# plt.axvline(mean_dmo, linestyle='--', color='limegreen')
# plt.axvline(gmean_dmo, linestyle=':', color='limegreen')
#
# plt.legend()

# SEPARATE Cv IN RADIAL DISTANCE (TWO BINS ONLY, IN 0.3 R200)
print()
print('Radial subplots')

plt.subplots(2, 2)
for i in range(1, 3):
    if i == 1:
        Grand_dmo = Grand_dmo_raw[Grand_dmo_raw[:, 2] < 0.3 * 220, :]
        Grand_dmo[:, 0] = Cv_Grand_points(Grand_dmo[:, 1], Grand_dmo[:, 0])
        Grand_hydro = Grand_hydro_raw[Grand_hydro_raw[:, 2] < 0.3 * 220, :]
        Grand_hydro[:, 0] = Cv_Grand_points(Grand_hydro[:, 1],
                                            Grand_hydro[:, 0])

    if i == 2:
        Grand_dmo = Grand_dmo_raw[Grand_dmo_raw[:, 2] > 0.3 * 220, :]
        Grand_dmo[:, 0] = Cv_Grand_points(Grand_dmo[:, 1], Grand_dmo[:, 0])
        Grand_hydro = Grand_hydro_raw[Grand_hydro_raw[:, 2] > 0.3 * 220, :]
        Grand_hydro[:, 0] = Cv_Grand_points(Grand_hydro[:, 1],
                                            Grand_hydro[:, 0])

    ax1 = plt.subplot(2, 2, i)
    plt.plot(Grand_dmo[:, 1], Grand_dmo[:, 0], '.k', label='DMO', markersize=3)
    plt.plot(Grand_hydro[:, 1], Grand_hydro[:, 0], '.', label='Hydro',
             color='limegreen', markersize=3)
    plt.axvline(6.4, color='k', alpha=0.5, linestyle='--')

    plt.xscale('log')
    plt.yscale('log')

    plt.ylim(5e3, 3e5)

    number_bins_dmo = 20
    number_bins_hydro = 18

    xx_dmo, yy_dmo, y20_dmo, y80_dmo = calculate_med(
        Grand_dmo, number_bins_dmo, perc_low=16, perc_high=84)
    xx_hydro, yy_hydro, y20_hydro, y80_hydro = calculate_med(
        Grand_hydro, number_bins_hydro, perc_low=16, perc_high=84)

    xx_dmo_mean, yy_dmo_mean = calculate_gmean(Grand_dmo, number_bins_dmo)
    xx_hydro_mean, yy_hydro_mean = calculate_gmean(
        Grand_hydro, number_bins_hydro)

    # Limit to the power law tendency
    hydroLimit = 11
    dmoLimit = 11

    # ------- Calculations of power laws assuming m from median value -----------------------
    print()

    print('DMO')
    fitsM_dmo, exps_dmo = power_law_from_median(yy_dmo, y20_dmo, y80_dmo,
                                                xx_dmo, dmoLimit)
    print('Scatter: %.3f and %.3f' % (
        exps_dmo[0] - exps_dmo[1], exps_dmo[2] - exps_dmo[0]))
    print()
    print()

    print('Hydro')
    fitsM_hydro, exps_hydro = power_law_from_median(yy_hydro, y20_hydro,
                                                    y80_hydro, xx_hydro,
                                                    hydroLimit)
    print('Scatter: %.3f and %.3f' % (
        exps_hydro[0] - exps_hydro[1], exps_hydro[2] - exps_hydro[0]))

    print()
    print('Mean')
    print('DMO')
    fitsM_dmo_mean, exps_dmo_mean = power_law_from_median(yy_dmo_mean, y20_dmo,
                                                          y80_dmo, xx_dmo_mean,
                                                          dmoLimit)
    print('Scatter: %.3f and %.3f' % (
        exps_dmo_mean[0] - exps_dmo_mean[1],
        exps_dmo_mean[2] - exps_dmo_mean[0]))
    print()
    print()
    print('Hydro')
    fitsM_hydro_mean, exps_hydro_mean = power_law_from_median(yy_hydro_mean,
                                                              y20_hydro,
                                                              y80_hydro,
                                                              xx_hydro_mean,
                                                              hydroLimit)
    print('Scatter: %.3f and %.3f' % (exps_hydro_mean[0] - exps_hydro_mean[1],
                                      exps_hydro_mean[2] - exps_hydro_mean[0]))

    plt.subplot(2, 2, i + 2, sharex=ax1, sharey=ax1)

    # xx_plot = np.logspace(np.log10(xx_dmo[0]), np.log10(xx_dmo[-1]))
    xx_plot = np.logspace(np.log10(xx_dmo[0]), np.log10(60))

    '''
    plt.fill_between(xx_plot, 10**exps_dmo[1]*xx_plot**fitsM_dmo,
                              10**exps_dmo[2]*xx_plot**fitsM_dmo,
                     color='grey', alpha=0.3, label=r'1 $\sigma$')

    plt.fill_between(xx_plot, 10**exps_hydro[1]*xx_plot**fitsM_hydro,
                              10**exps_hydro[2]*xx_plot**fitsM_hydro,
                     color='limegreen', alpha=0.4)
    '''
    plt.plot(xx_plot, Cv_Mol2021_redshift0(xx_plot), color='red',
             label='Moliné+21', alpha=0.5, linewidth=3)

    plt.plot(xx_plot, 10 ** exps_dmo[0] * xx_plot ** fitsM_dmo, '-k',
             linewidth=3)
    plt.plot(xx_plot, 10 ** exps_hydro[0] * xx_plot ** fitsM_hydro, '-g',
             linewidth=3)

    plt.plot(xx_plot, 10 ** exps_dmo_mean[0] * xx_plot ** fitsM_dmo_mean,
             '--k', linewidth=3)
    plt.plot(xx_plot, 10 ** exps_hydro_mean[0] * xx_plot ** fitsM_hydro_mean,
             '--g', linewidth=3)

    plt.plot(xx_dmo, yy_dmo, '.k', label='DMO', ms=13)
    plt.plot(xx_hydro, yy_hydro, '.g', label='Hydro', ms=13)

    plt.plot(xx_dmo_mean, yy_dmo_mean, 'xk', ms=13)
    plt.plot(xx_hydro_mean, yy_hydro_mean, 'xg', ms=13)

    # xx_dmo, yy_dmo, y20_dmo, y80_dmo

    moline_fits = curve_fit(log10MOline21,
                            xdata=xx_dmo[xx_dmo > dmoLimit],
                            ydata=np.log10(
                                yy_dmo[xx_dmo > dmoLimit]),
                            p0=[1.75e5, -0.90368, 0.2749, -0.028],
                            bounds=([-np.inf, -np.inf, -np.inf, -np.inf],
                                    [+np.inf, +np.inf, +np.inf, +np.inf]))

    print(moline_fits)

    plt.plot(xx_plot, Cv_Mol2021_redshift0(V=xx_plot,
                                           c0=moline_fits[0][0],
                                           c1=moline_fits[0][1],
                                           c2=moline_fits[0][2],
                                           c3=moline_fits[0][3]),
             color='k',
             label='Moliné+21 fit', alpha=0.5, linewidth=3)

    moline_fits = curve_fit(log10MOline21,
                            xdata=xx_hydro[
                                xx_hydro > hydroLimit],
                            ydata=np.log10(
                                yy_hydro[
                                    xx_hydro > hydroLimit]),
                            p0=[1.e4, -0.90368, 0.2749, -0.028],
                            bounds=([-np.inf, -np.inf, -np.inf, -np.inf],
                                    [9e4, +np.inf, +np.inf, +np.inf]))

    plt.plot(xx_plot, Cv_Mol2021_redshift0(V=xx_plot,
                                           c0=moline_fits[0][0],
                                           c1=moline_fits[0][1],
                                           c2=moline_fits[0][2],
                                           c3=moline_fits[0][3]),
             color='g',
             alpha=0.5, linewidth=3)

    # GMEAN
    # moline_fits = curve_fit(log10MOline21,
    #                         xdata=xx_dmo_mean[xx_dmo_mean > dmoLimit],
    #                         ydata=np.log10(
    #                             yy_dmo_mean[xx_dmo_mean > dmoLimit]),
    #                         p0=[1.75e5, -0.90368, 0.2749, -0.028],
    #                         bounds=([-np.inf, -np.inf, -np.inf, -np.inf],
    #                                 [+np.inf, +np.inf, +np.inf, +np.inf]))
    #
    #
    # plt.plot(xx_plot, Cv_Mol2021_redshift0(V=xx_plot,
    #                                        c0=moline_fits[0][0],
    #                                        c1=moline_fits[0][1],
    #                                        c2=moline_fits[0][2],
    #                                        c3=moline_fits[0][3]),
    #          color='k', linestyle='--',
    #          alpha=0.5, linewidth=3)

    # moline_fits = curve_fit(log10MOline21,
    #                         xdata=xx_hydro_mean[
    #                             xx_hydro_mean > hydroLimit],
    #                         ydata=np.log10(
    #                             yy_hydro_mean[
    #                                 xx_hydro_mean > hydroLimit]),
    #                         p0=moline_fits[0],
    #                         bounds=([-np.inf, -np.inf, -np.inf, -np.inf],
    #                                 [+np.inf, +np.inf, +np.inf, +np.inf]))
    #
    # print(moline_fits)
    #
    # plt.plot(xx_plot, Cv_Mol2021_redshift0(V=xx_plot,
    #                                        c0=moline_fits[0][0],
    #                                        c1=moline_fits[0][1],
    #                                        c2=moline_fits[0][2],
    #                                        c3=moline_fits[0][3]),
    #          color='g', linestyle='--',
    #          alpha=0.5, linewidth=3)

    plt.axvline(dmoLimit, linestyle='-', color='k', alpha=0.3, linewidth=2)
    plt.axvline(hydroLimit, linestyle='-', color='g', alpha=0.3, linewidth=2)

    plt.xscale('log')
    plt.yscale('log')

    plt.xlabel(r'$V_\mathrm{max}$ [km s$^{-1}$]', size=28)

    plt.annotate('- median \n -- gmean', (20, 6500))

    # plt.xlim(1.3, 53)

plt.subplot(221)
plt.title('Subs < 0.3 * 220')
plt.ylabel(r'c$_\mathrm{V}$', size=28)

plt.subplot(222)
plt.title('Subs > 0.3 * 220')

plt.subplot(223)
plt.ylabel(r'c$_\mathrm{V}$', size=28)
#
#
#
# #%%
#
# print()
# print("COMPARISON BETWEEN Cv AND C200 (assuming NFW)")
#
#
#
# C200_dmo,   C20_dmo,   C80_dmo   = C200_from_Cv(yy_dmo,   y20_dmo,   y80_dmo,   xx_dmo)
# C200_hydro, C20_hydro, C80_hydro = C200_from_Cv(yy_hydro, y20_hydro, y80_hydro, xx_hydro)
#
#
# masses_dmo   = mass_from_Vmax(xx_dmo,   yy_dmo,   C200_dmo)
# masses_hydro = mass_from_Vmax(xx_hydro, yy_hydro, C200_hydro)
#
# dmoLimit_mass = 1.5e8
# hydroLimit_mass = 1.5e8
#
# print('DMO')
# fitsM_dmo_mass,   exps_dmo_mass   = power_law_from_median(C200_dmo,   C20_dmo,   C80_dmo,   masses_dmo,   dmoLimit_mass)
# print('Scatter: %.3f and %.3f' %(exps_dmo_mass[0]-exps_dmo_mass[1], exps_dmo_mass[2]-exps_dmo_mass[0]))
# print()
# print('Hydro')
# fitsM_hydro_mass, exps_hydro_mass = power_law_from_median(C200_hydro, C20_hydro, C80_hydro, masses_hydro, hydroLimit_mass)
# print('Scatter: %.3f and %.3f' %(exps_hydro_mass[0]-exps_hydro_mass[1], exps_hydro_mass[2]-exps_hydro_mass[0]))
#
# #print()
# #print()
# #print('Dmo')
# #bb_dmo   = sigma_from_2080(exps_dmo)
# #print()
# #print('Hydro')
# #bb_hydro = sigma_from_2080(exps_hydro)
#
#
# #%%
# #---------------------------------------------------------------------------------------------------
#
# plt.figure(figsize=(9,9))
#
# xx_plot = np.logspace(np.log10(masses_dmo[0]), np.log10(masses_dmo[-1]))
#
# colors= ['wheat', 'orange', 'darkorange']
# col=0
# for dist in [0.1, 0.3, 1.]:
#     orig = Cm_Mol16(xx_plot, R200*dist)
#     plt.plot(xx_plot, orig, '-', color=colors[col], label=(r'Mol+17 x$_\mathrm{sub}$ = ' + str(dist)))
#     col += 1
#
# plt.plot(masses_dmo,   C200_dmo,   '.', color='k', label='DMO')
# plt.plot(masses_hydro, C200_hydro, '.', color='g', label='Hydro')
#
# #plt.axvline(LIMIT, color=colour, alpha=0.5)
#
#
# plt.plot(xx_plot, 10**exps_dmo_mass  [0]*xx_plot**fitsM_dmo_mass,   '-k')
# plt.plot(xx_plot, 10**exps_hydro_mass[0]*xx_plot**fitsM_hydro_mass, '-g')
#
# plt.fill_between(xx_plot, 10**exps_dmo_mass[1]*xx_plot**fitsM_dmo_mass,
#                           10**exps_dmo_mass[2]*xx_plot**fitsM_dmo_mass,
#                  color='k', alpha=0.3, label=r'1 $\sigma$')
#
# plt.fill_between(xx_plot, 10**exps_hydro_mass[1]*xx_plot**fitsM_hydro_mass,
#                           10**exps_hydro_mass[2]*xx_plot**fitsM_hydro_mass,
#                  color='g', alpha=0.3)
#
#
#
# plt.ylabel(r'$c_{200}$', size=22)
# plt.xlabel(r'$\mathrm{M}$ ($\mathrm{M}_{\mathrm{sun}}$)', size=20)
#
#
# plt.xscale('log')
# plt.yscale('log')
# plt.legend()
#
#
# plt.figure()
# plt.plot(xx_dmo, masses_dmo, '.k', label='DMO')
# plt.plot(xx_hydro, masses_hydro, '.g', label='Hydro')
#
# Rmax = 56.7728 # from VLII website
# Vmax = 201.033 # from VLII website
# R_vir = 402. # from Pieri
# r_s_MW = 21.
#
# print('MW mass from NFW transformation: %.2e Msun' %mass_from_Vmax(Vmax, Rmax, R_vir/r_s_MW))
#
# plt.plot(Vmax, mass_from_Vmax(Vmax, Rmax, R_vir/r_s_MW), '+r', label='MW')
#
# plt.xscale('log')
# plt.yscale('log')
# plt.legend()
#
# plt.ylabel(r'$\mathrm{M}$ ($\mathrm{M}_{\mathrm{sun}}$)', size=20)
# plt.xlabel(r'V$_{max}$ [km/s]', size=22)
#
#
# #%%
#
# xxx = np.logspace(0, np.log10(60))
# #plt.close('all')
# plt.figure()
#
# plt.plot(xxx, 10**(exps_dmo[0]-exps_hydro[0]) * xxx**(fitsM_dmo-fitsM_hydro))
#
#
# plt.xscale('log')
# #plt.legend()
#
# plt.xlabel(r'V$_{max}$ [km/s]', size=22)
# plt.ylabel(r'$\frac{c_{V, DMO}}{c_{V, Hydro}}$', size=26)
plt.show()


"""Implements Distributed Prioritized Experience Replay.

https://arxiv.org/abs/1803.00933"""

from __future__ import absolute_import
from __future__ import division
from __future__ import print_function

import collections
import os
import random
import time
import threading

import numpy as np
from six.moves import queue

import ray
from ray.rllib.evaluation.sample_batch import SampleBatch, DEFAULT_POLICY_ID, \
    MultiAgentBatch
from ray.rllib.optimizers.policy_optimizer import PolicyOptimizer
from ray.rllib.optimizers.replay_buffer import PrioritizedReplayBuffer
from ray.rllib.utils.actors import TaskPool, create_colocated
from ray.rllib.utils.timer import TimerStat
from ray.rllib.utils.window_stat import WindowStat

SAMPLE_QUEUE_DEPTH = 2
REPLAY_QUEUE_DEPTH = 4
LEARNER_QUEUE_MAX_SIZE = 16

@ray.remote(num_cpus=0)
class ReplayActor(object):
    """A replay buffer shard.

    Ray actors are single-threaded, so for scalability multiple replay actors
    may be created to increase parallelism."""

    def __init__(self, num_shards, learning_starts, buffer_size,
                 train_batch_size, prioritized_replay_alpha,
                 prioritized_replay_beta, prioritized_replay_eps):
        self.replay_starts = learning_starts // num_shards
        self.buffer_size = buffer_size // num_shards
        self.train_batch_size = train_batch_size
        self.prioritized_replay_beta = prioritized_replay_beta
        self.prioritized_replay_eps = prioritized_replay_eps

        def new_buffer():
            return PrioritizedReplayBuffer(
                self.buffer_size, alpha=prioritized_replay_alpha)

        self.replay_buffers = collections.defaultdict(new_buffer)

        # Metrics
        self.add_batch_timer = TimerStat()
        self.replay_timer = TimerStat()
        self.update_priorities_timer = TimerStat()
        self.num_added = 0

    def get_host(self):
        return os.uname()[1]

    def add_batch(self, batch):
        # Handle everything as if multiagent
        if isinstance(batch, SampleBatch):
            batch = MultiAgentBatch({DEFAULT_POLICY_ID: batch}, batch.count)
        with self.add_batch_timer:
            for policy_id, s in batch.policy_batches.items():
                for row in s.rows():
                    self.replay_buffers[policy_id].add(
                        row["obs"], row["actions"], row["rewards"],
                        row["new_obs"], row["dones"], row["weights"])
        self.num_added += batch.count

    def replay(self):
        if self.num_added < self.replay_starts:
            return None

        with self.replay_timer:
            samples = {}
            for policy_id, replay_buffer in self.replay_buffers.items():
                (obses_t, actions, rewards, obses_tp1, dones, weights,
                 batch_indexes) = replay_buffer.sample(
                     self.train_batch_size, beta=self.prioritized_replay_beta)
                samples[policy_id] = SampleBatch({
                    "obs": obses_t,
                    "actions": actions,
                    "rewards": rewards,
                    "new_obs": obses_tp1,
                    "dones": dones,
                    "weights": weights,
                    "batch_indexes": batch_indexes
                })
            return MultiAgentBatch(samples, self.train_batch_size)

    def update_priorities(self, prio_dict):
        with self.update_priorities_timer:
            for policy_id, (batch_indexes, td_errors) in prio_dict.items():
                new_priorities = (
                    np.abs(td_errors) + self.prioritized_replay_eps)
                self.replay_buffers[policy_id].update_priorities(
                    batch_indexes, new_priorities)

    def stats(self, debug=False):
        stat = {
            "add_batch_time_ms": round(1000 * self.add_batch_timer.mean, 3),
            "replay_time_ms": round(1000 * self.replay_timer.mean, 3),
            "update_priorities_time_ms": round(
                1000 * self.update_priorities_timer.mean, 3),
        }
        for policy_id, replay_buffer in self.replay_buffers.items():
            stat.update({
                "policy_{}".format(policy_id): replay_buffer.stats(debug=debug)
            })
        return stat

class LearnerThread(threading.Thread):
    """Background thread that updates the local model from replay data.

    The learner thread communicates with the main thread through Queues. This
    is needed since Ray operations can only be run on the main thread. In
    addition, moving heavyweight gradient ops session runs off the main thread
    improves overall throughput.
    """

    def __init__(self, local_evaluator):
        threading.Thread.__init__(self)
        self.learner_queue_size = WindowStat("size", 50)
        self.local_evaluator = local_evaluator
        self.inqueue = queue.Queue(maxsize=LEARNER_QUEUE_MAX_SIZE)
        self.outqueue = queue.Queue()
        self.queue_timer = TimerStat()
        self.grad_timer = TimerStat()
        self.daemon = True
        self.weights_updated = False

    def run(self):
        while True:
            self.step()

    def step(self):
        with self.queue_timer:
            ra, replay = self.inqueue.get()
        if replay is not None:
            prio_dict = {}
            with self.grad_timer:
                grad_out = self.local_evaluator.compute_apply(replay)
                for pid, info in grad_out.items():
                    prio_dict[pid] = (
                        replay.policy_batches[pid]["batch_indexes"],
                        info["td_error"])
            # send `replay` back also so that it gets released by the original
            # thread: https://github.com/ray-project/ray/issues/2610
            self.outqueue.put((ra, replay, prio_dict, replay.count))
        self.learner_queue_size.push(self.inqueue.qsize())
        self.weights_updated = True

class AsyncReplayOptimizer(PolicyOptimizer):
    """Main event loop of the Ape-X optimizer (async sampling with replay).

    This class coordinates the data transfers between the learner thread,
    remote evaluators (Ape-X actors), and replay buffer actors.

    This optimizer requires that policy evaluators return an additional
    "td_error" array in the info return of compute_gradients(). This error
    term will be used for sample prioritization."""

    def _init(self,
              learning_starts=1000,
              buffer_size=10000,
              prioritized_replay=True,
              prioritized_replay_alpha=0.6,
              prioritized_replay_beta=0.4,
              prioritized_replay_eps=1e-6,
              train_batch_size=512,
              sample_batch_size=50,
              num_replay_buffer_shards=1,
              max_weight_sync_delay=400,
              debug=False):

        self.debug = debug
        self.replay_starts = learning_starts
        self.prioritized_replay_beta = prioritized_replay_beta
        self.prioritized_replay_eps = prioritized_replay_eps
        self.max_weight_sync_delay = max_weight_sync_delay

        self.learner = LearnerThread(self.local_evaluator)
        self.learner.start()

        self.replay_actors = create_colocated(ReplayActor, [
            num_replay_buffer_shards,
            learning_starts,
            buffer_size,
            train_batch_size,
            prioritized_replay_alpha,
            prioritized_replay_beta,
            prioritized_replay_eps,
        ], num_replay_buffer_shards)

        # Stats
        self.timers = {
            k: TimerStat()
            for k in [
                "put_weights", "get_samples", "sample_processing",
                "replay_processing", "update_priorities", "train", "sample"
            ]
        }
        self.num_weight_syncs = 0
        self.num_samples_dropped = 0
        self.learning_started = False

        # Number of worker steps since the last weight update
        self.steps_since_update = {}

        # Otherwise kick of replay tasks for local gradient updates
        self.replay_tasks = TaskPool()
        for ra in self.replay_actors:
            for _ in range(REPLAY_QUEUE_DEPTH):
                self.replay_tasks.add(ra, ra.replay.remote())

        # Kick off async background sampling
        self.sample_tasks = TaskPool()
        if self.remote_evaluators:
            self.set_evaluators(self.remote_evaluators)

    # For https://github.com/ray-project/ray/issues/2541 only
    def set_evaluators(self, remote_evaluators):
        self.remote_evaluators = remote_evaluators
        weights = self.local_evaluator.get_weights()
        for ev in self.remote_evaluators:
            ev.set_weights.remote(weights)
            self.steps_since_update[ev] = 0
            for _ in range(SAMPLE_QUEUE_DEPTH):
                self.sample_tasks.add(ev, ev.sample_with_count.remote())

    def step(self):
        assert len(self.remote_evaluators) > 0
        start = time.time()
        sample_timesteps, train_timesteps = self._step()
        time_delta = time.time() - start
        self.timers["sample"].push(time_delta)
        self.timers["sample"].push_units_processed(sample_timesteps)
        if train_timesteps > 0:
            self.learning_started = True
        if self.learning_started:
            self.timers["train"].push(time_delta)
            self.timers["train"].push_units_processed(train_timesteps)
        self.num_steps_sampled += sample_timesteps
        self.num_steps_trained += train_timesteps

    def _step(self):
        sample_timesteps, train_timesteps = 0, 0
        weights = None

        with self.timers["sample_processing"]:
            completed = list(self.sample_tasks.completed())
            counts = ray.get([c[1][1] for c in completed])
            for i, (ev, (sample_batch, count)) in enumerate(completed):
                sample_timesteps += counts[i]

                # Send the data to the replay buffer
                random.choice(
                    self.replay_actors).add_batch.remote(sample_batch)

                # Update weights if needed
                self.steps_since_update[ev] += counts[i]
                if self.steps_since_update[ev] >= self.max_weight_sync_delay:
                    # Note that it's important to pull new weights once
                    # updated to avoid excessive correlation between actors
                    if weights is None or self.learner.weights_updated:
                        self.learner.weights_updated = False
                        with self.timers["put_weights"]:
                            weights = ray.put(
                                self.local_evaluator.get_weights())
                    ev.set_weights.remote(weights)
                    self.num_weight_syncs += 1
                    self.steps_since_update[ev] = 0

                # Kick off another sample request
                self.sample_tasks.add(ev, ev.sample_with_count.remote())

        with self.timers["replay_processing"]:
            for ra, replay in self.replay_tasks.completed():
                self.replay_tasks.add(ra, ra.replay.remote())
                if self.learner.inqueue.full():
                    self.num_samples_dropped += 1
                else:
                    with self.timers["get_samples"]:
                        samples = ray.get(replay)
                    self.learner.inqueue.put((ra, samples))

        with self.timers["update_priorities"]:
            while not self.learner.outqueue.empty():
                ra, _, prio_dict, count = self.learner.outqueue.get()
                ra.update_priorities.remote(prio_dict)
                train_timesteps += count

        return sample_timesteps, train_timesteps

    def stats(self):
        replay_stats = ray.get(self.replay_actors[0].stats.remote(self.debug))
        timing = {
            "{}_time_ms".format(k): round(1000 * self.timers[k].mean, 3)
            for k in self.timers
        }
        timing["learner_grad_time_ms"] = round(
            1000 * self.learner.grad_timer.mean, 3)
        timing["learner_dequeue_time_ms"] = round(
            1000 * self.learner.queue_timer.mean, 3)
        stats = {
            "sample_throughput": round(self.timers["sample"].mean_throughput,
                                       3),
            "train_throughput": round(self.timers["train"].mean_throughput, 3),
            "num_weight_syncs": self.num_weight_syncs,
            "num_samples_dropped": self.num_samples_dropped,
            "learner_queue": self.learner.learner_queue_size.stats(),
            "replay_shard_0": replay_stats,
        }
        debug_stats = {
            "timing_breakdown": timing,
            "pending_sample_tasks": self.sample_tasks.count,
            "pending_replay_tasks": self.replay_tasks.count,
        }
        if self.debug:
            stats.update(debug_stats)
        return dict(PolicyOptimizer.stats(self), **stats)


from django.conf.urls import url
from . import views

app_name = 'leaderboard'

urlpatterns = [
    # home page
    url(r'^$', views.index, name='index'),

    # teams page
    url(r'^teams/$', views.view_teams, name='viewteams'),

    # matches page
    url(r'^matches/$', views.view_matches, name='viewmatches'),

    # leaderboard page
    url(r'^topteams/$', views.top_teams, name='topteams'),

    url(r'^registerteam/$', views.team_add_view, name='team_add'),

    url(r'^creatematch/$', views.match_add_view, name='match_add'),

    url(r'^scorecard/$', views.score_card, name='scorecard'),
]


from fastapi import HTTPException, APIRouter
from app.model import order_model
from app.security.dependencies import get_database

order_router = APIRouter()

# Function to check the Order Status == "Completed" and return respected Data to user
async def completed_order_data(status: str):
    try:
        for order_status in get_database.db:
            if status == "Completed":
                return HTTPException(status_code=200, detail={"order_status": order_status})
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

# Function to check the Order Status == "Running" and return respected Data to user
async def running_order_data(status: str, db=None):
    try:
        for order_status in get_database.db:
            if status == "Running":
                return HTTPException(status_code=200, detail={"order_status": order_status})
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

# Function to check the Order Status == "Archived" and return respected Data to user
async def archived_order_data(status: str):
    try:
        for order_status in get_database.db:
            if status == "Archived":
                return HTTPException(status_code=200, detail={"order_status": order_status})
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

# API to Show Order data to users
@order_router.get("/view_orders_data")
async def view_order_data(order_id: int, db=None):
    try:
        data = await get_database(db).find_one({"order_id": order_id})
        if data:
            return HTTPException(status_code=200, detail="Data Found!")
        else:
            return HTTPException(status_code=404, detail="ID Does Not Exists!")
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

# API to set new order and data to DataBase
@order_router.post("/set_order_data")
async def set_order_data(orders: order_model.Order, db=None):
    try:
        data = await get_database(db).find_one({"order_id": orders.order_id})
        if data:
            return HTTPException(status_code=409, detail="ID Already Exists!")
        else:
            await get_database.db.insert_one(orders.dict())
            return HTTPException(status_code=200, detail="Data Inserted Successfully!")
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))

# API to Delete an order and its data from DataBase
@order_router.delete("/delete_data")
async def delete_data(order_id: int, db=None):
    try:
        data = await get_database(db).find_one({"order_id": order_id})
        if data:
            await get_database.db.delete_one({"order_id": order_id})
            return HTTPException(status_code=201, detail="Data Deleted Successfully!")
        else:
            return HTTPException(status_code=404, detail="ID Not Found!")
    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))


## secante tasa de convergencia
import numpy as np
import scipy.optimize as opt
import matplotlib.pyplot as plt
def funcion1(x):
    return - (np.sqrt(3.0*x)**(2.0/5.0))+(x**3.0)*np.cos(3.0*x)+4.0*(x**2.0) - 7.0
def funcion2(x):
    return -x**(0.25)+np.sin(3.5*x)+4*x**(0.5)+2*x - 5
def secante(f,tol):
    x0 = 2.0
    x1 = 1.9
    tolf = tol
    tolx = tolf
    x2prev = x1
    r_candidatas = np.array([])
    iteraciones = 0
    while True:
        iteraciones +=1
        x2 = x1 - (f(x1) * (x1 - x0) / (f(x1) - f(x0)))
        r_candidatas = np.append(r_candidatas, x2)
        if np.abs(x2 - x2prev) <= tolx:
            break
        if np.abs(f(x2)) <= tolf:
            break
        x0 = x1
        x1 = x2
        x2prev = x2
    r_verdadero = opt.fsolve(funcion1, x1)
    error_estimado = np.abs(r_candidatas - r_verdadero)
    tam = np.size(error_estimado)
    tasas_convergencias = (np.log10(error_estimado[1:tam - 1] / error_estimado[2:tam]) / np.log10(
        error_estimado[0:tam - 2] / error_estimado[1:tam - 1]))
    print("Secante: con {} iteraciones, {} de tolerancia, la raiz es: {} y tasa de convergencia entre {} y {} ".format(iteraciones, tolx, x2, tasas_convergencias[-2], tasas_convergencias[-1]))
    iter_array = np.arange(1.0,np.size(tasas_convergencias)+1)
    plt.figure()
    plt.plot(iter_array,tasas_convergencias)
    plt.plot(iter_array,tasas_convergencias,"or")
    plt.show()

print("f1")
secante(funcion1,10**-10)
secante(funcion1,10**-15)
print("f2")
secante(funcion2,10**-5)
secante(funcion2,10**-15)


from django.shortcuts import render, redirect
from django.contrib import messages
from django.core.mail import send_mail
from .models import Contact

def contact(request):
  if request.method == 'POST':
    organization_id = request.POST['organization_id']
    organization = request.POST['organization']
    name = request.POST['name']
    email = request.POST['email']
    phone = request.POST['phone']
    message = request.POST['message']
    user_id = request.POST['user_id']
    organization_email = request.POST['organization_email']

    #  Check if user has made inquiry already
    # if request.user.is_authenticated:
    #   user_id = request.user.id
    #   has_contacted = Contact.objects.all().filter(organization_id=organization_id, user_id=user_id)
    #   if has_contacted:
    #     messages.error(request, 'You have already made an inquiry for this organization')
    #     return redirect('/organizations/'+organization_id)

    contact = Contact(organization=organization, organization_id=organization_id, name=name, email=email, phone=phone, message=message, user_id=user_id )

    contact.save()

    # Send email
    send_mail(
      'Organization Inquiry - Pehel',
      'There has been an inquiry for ' + organization + '. Sign into the admin panel for more info.',
      'pikachu.777.222@gmail.com',
      [organization_email, 'jjaskirat.ssingh@gmail.com'],
      fail_silently=False
    )

    messages.success(request, 'Your request has been submitted, an organization representative will get back to you soon.')
    return redirect('/organizations/'+organization_id)


import requests
import json
from time import sleep

class APIRequests:
    def __init__(self, Address):
        self.UrlDict = {'base':'https://api.helium.io/v1/', 'hotspot':'https://api.helium.io/v1/hotspots/'}
        self.Address = Address
        # self.Stats = requests.get("https://api.helium.io/v1/stats")
        self.status = None

    def GetHotspotInfo(self, event = None):
        h1 = requests.get(f"{self.UrlDict['hotspot']}{self.Address}")
        if h1.status_code == 429:
            sleep(int(h1.headers['retry-after'])+1)
            self.GetHotspotInfo()
        self.rJson1 = json.loads(h1.content)['data'] if json.loads(h1.content)['data'] else {'data':{'timestamp_added': 0}}
        return self.rJson1

    def GetHotspotActivity(self, cursor = None, filters = None, event = None):
        if not cursor:
            h2 = requests.get(f"{self.UrlDict['hotspot']}{self.Address}/activity?{filters}")
            cursor = json.loads(h2.content)['cursor']
        h2 = requests.get(f"{self.UrlDict['hotspot']}{self.Address}/activity?{cursor}")
        self.rJson2 = json.loads(h2.content)
        return self.rJson2
    def GetHotspotLocation(self, distance = "15000", event = None):
        h3 = requests.get(f"{self.UrlDict['hotspot']}location/distance?lat={self.rJson1['lat']}&lon={self.rJson1['lon']}&distance={distance}")

if __name__ == "__main__":
    apiObject = APIRequests(input("Hotspot Address"))