# assignment4.py
#
# Contains the implementation of a replicated fault-tolerant
# key-value store that provides casual consistency.

from flask import Flask, jsonify, request, abort
from flask_restful import Api, Resource, reqparse
from requests.exceptions import Timeout
from uhashring import HashRing
from mmh3 import hash as m3h
import json
import os
import requests as req
import numpy as np
import time

app = Flask(__name__)
api = Api(app)

# Tweak variable, timeout until a replica is considered failed.
# Only for accessing other replicas!
timeoutduration = 1

# Global variable, dictionary of keys and values.
dic = {}
# Global variable, array of ip:port combinations of all other operational replicas.
viewstore = None
# Global variable, current ip:port combination.
socketaddr = ""
# Global variable for causal-metadata.
causalmetadata = ""

vector_clock = []
total_differences = 0
replicaNumber = None
NumberOfReplicas = None

#global var containing all known shards
shards = []
shard_id = None
shard_members = {}

# Used to delete an address from viewstore and broadcast that
# address to the rest of the replicas. This function is triggered by
# try:except statements when trying to access all of the replicas in
# the current viewstore array.
def deleteaddr(targetaddr):
    print("Initializing delete and broadcast...")
    payload = {"socket-address": targetaddr}
    if targetaddr in viewstore:
        # Delete from local view
        print("trying to sending a delete to own view")
        try:

            request = req.delete("http://" + socketaddr + "/key-value-store-view", data=payload,
                                 timeout=timeoutduration)
            print("success with a response: ", request)
        except req.exceptions.RequestException as ex:
            print("sending viewstore request to self failed....")
        # Broadcast to other views.
        print("  - Broadcasting DELETE to other replicas...")

        #	DEBUG PAYLOAD PRINT

        for replicaaddr in viewstore:
            if replicaaddr != socketaddr:
                print("  - Sending DELETE to " + replicaaddr + "...")
                try:
                    request = req.delete("http://" + replicaaddr + "/key-value-store-view", data=payload,
                                         timeout=timeoutduration)
                    print("request view-delete debug", request)
                    print("   - Successful")
                except req.exceptions.RequestException as ex:
                    # WARNING, a replica in the view could not be reached! time to call key-value-store-view DELETE.
                    print("   WARNING - Unable to reach replica address " + replicaaddr + "! Exception Raised: ", ex)
                    # Recursion can happen if more than
                    # one replica address goes down.
                    deleteaddr(replicaaddr)

#Calculates total differences between current clock and received metadata clock
#Expected clock
def calculateDifferences(combined_clock):
    total_differences = 0
    for a,b in combined_clock:

        if a != b:
            total_differences += abs( int(a) - int(b) )

    return total_differences

"""
this differentiates the replicas from one another
and can use this information to automatically update
causalmetadata per requests

  This works by finding the replica's socket address in a list of the view addresses
  each replica should have its own socket address variable per docker env
"""

def getReplicaNumber():
    global viewstore
    viewstoreList = viewstore
    print("view: ", viewstoreList)
    print("socketaddress: ", socketaddr)
    global replicaNumber
    global NumberOfReplicas
    view = checkOtherViews(viewstoreList)
    if view != None:
        print("\n\nNEW VIEW")
        print("view: ", view, "\n\n")

    for address in viewstoreList:
        if address == socketaddr:
            addressIndex = viewstoreList.index(address)
            print("found replica number at list index: ", viewstoreList.index(address), "\n\n")
            replicaNumber = addressIndex
            print("New Replica Number: ", replicaNumber)
    return replicaNumber

def checkOtherViews(viewstore):
    print("view store: ", viewstore)
    storedViews = []
    differentViews = []
    for address in viewstore:
        if address != socketaddr:
            try:
                print("trying to check the view")
                checkView = req.get("http://" + address + "/key-value-store-view", timeout=1)
                checkView = checkView.json()
                responseView = checkView['view']
                responseView = responseView.split(",")
                print("response view: ", responseView)
                storedViews.append(responseView)
            except req.exceptions.RequestException as ex:
                print("Could not access view... moving on")

    print("stored views: ", storedViews)
    for views in storedViews:
        if len(viewstore) < len(views):
            print(viewstore, " is less than: ", views)
            differentViews.append(views)
        elif len(viewstore) > len(views):
            print("current views is greater than others, need to PUT itself")
            for missView in views:
                payload = {"socket-address": socketaddr}
                try:
                    print("trying to PUT itself to other views")
                    putView = req.put("http://" + missView + "/key-value-store-view", payload, timeout=1)
                    print("response from PUT'ing itself in other views: ", putView.json())
                except req.exceptions.RequestException as xd:
                    print("Could not PUT itself to other views...")
    if len(differentViews) != 0:
        view = differentViews[0]
        return view
    else:
        return None

#Sort socket address/view addresses helper functions
def isolateIP(ip):
    return tuple(int(part) for part in ip.split('.'))

def sort_key(item):
    return isolateIP(item[0])

"""
Checks to see if a replica is behind in terms of causal consitancy. If it is, the replica should send a GET request
to other replicas and compare vector clock. If the returned vector clock is equal to the current vector clock,
replica should update the it's KVS with that replica's version of it. If the replica is behind, it should return the KVS
dictionary, otherwise it should return None.
"""
def checkVersion(currentCopy, receivedCopy, replicaNumber, key):
    differencesIndex = {}
    seen = []
    for i in range(len(currentCopy)):
        if currentCopy[i] != receivedCopy[i]:
            #replica was down, request may be wrong
            print("Here is the data: ")
            if currentCopy[i] < receivedCopy[i]:
                print("current replica is behind")
                differencesIndex[replicaNumber] = 'behind'
                vc = receivedCopy
                # iterate through the replicas
                for address in shard_members[shard_id]:
                    # if we are not dealing with the replica responding to the client
                    if address != socketaddr:
                        # We want to extract the vector clock
                        try:
                            r = req.get("http://" + address +"/key-value-store/"+str(key), timeout=5)
                            if r.status_code != 404:
                                vc = r.json()['causal-metadata']

                                # if the vector clock matches, return the KVS of that replica
                                kvs = None
                                if vc.split(" ") == receivedCopy:
                                    print("Sending GET to", address)
                                    try:
                                        kvs = req.get("http://" + address + "/kvs", timeout=5)
                                        print(kvs.json())
                                    except req.exceptions.RequestException as ex:
                                        print("could not get from ", address, ex)

                                # print("When trying to update replica, failed to reach replica:", address)
                                # iterate through each key in the replica's dictionary and update each key in the current replica's dictionary
                                # basically get it up to date
                                if kvs:
                                    for key in kvs.json():
                                        dic[key] = kvs.json()[key]
                                print("DIC:", dic)
                        except req.exceptions.RequestException as ex:
                            print("could not get from ", address, ex)
                        #this replica contains the key value store and is not behind
                if vc == receivedCopy:
                    return vc
                else:
                    return vc.split(" ")
            else:
                print("current replica is advanced??? returning -99")
                differencesIndex[replicaNumber] = 'advanced'
                return -99
    return None

# /key-value-store - same as assignment2, however also includes
# forwarding functionality for PUT and DELETE.
class Store(Resource):
    def get(self,key):
        print("(Log Message)[STORE] Initiating get!")
        print("causal: ", causalmetadata, flush=True)
        print("length of causal: ", len(causalmetadata.split(" ")), flush=True)
        print("length of viewstore: ", len(viewstore), flush=True)

        shardid = getShardID(key,hr)
        print(shardid, flush=True)
        if shardid != shard_id:
            print("Caught wrong GET shardid", flush=True)
            nodelist = shard_members[shardid]
            fwdget = None
            #Try to forward the GET to each node, if one succeeds, break loop
            for node in nodelist:
                try:
                    fwdget = req.get("http://" + node +"/key-value-store/"+str(key), timeout=timeoutduration)
                    if fwdget.status_code == 200 or fwdget.status_code == 201:
                        break
                except:
                    print("   WARNING - Unable to reach replica address " + replicaaddr + "! Exception Raised: ", ex)
            if fwdget != None:
                return fwdget.json(), fwdget.status_code

        if key not in dic:
            return {'doesExist':False,'error':'Key does not exist','message':'Error in GET'}, 404
        return {"message":"Retrieved successfully", "causal-metadata": causalmetadata, "value": dic.get(key)}, 200

        # else:
        #     #send request
        #     # nodelist = shard_members['shardid']
        #     # nodetosend = nodelist[0]

        #     # payload = {"message":"Retrieved successfully", "causal-metadata": causalmetadata, "value": dic.get(key)}
        #     return

    def put(self,key):
        global causalmetadata
        global total_differences
        replicaNumber = getReplicaNumber()
        #replicaNumber is the index of the current replica
        #we can use this with the vector clock to figure out which one
        parser = reqparse.RequestParser(bundle_errors=True)
        parser.add_argument("value", type=str)
        parser.add_argument("causal-metadata", type=str)
        parser.add_argument("node", type=int)
        args = parser.parse_args()
        print(args, flush=True)

        # parsing meta-data
        meta = str(args["causal-metadata"])
        #catch wrong shard-id requests here
        shardid = getShardID(key, hr)
        if shardid != shard_id:
            #caught the wrong shard_id in PUT
            print("Caught wrong shardid, forwarding...")
            nodelist = shard_members[shardid]
            valuevar = str(args["value"])
            payload = {"value": valuevar, "causal-metadata": meta}
            fwdRequest = None

            for node in nodelist:
                try:
                    print("Trying to forward PUT to ", node)
                    fwdRequest = req.put("http://" + node + "/key-value-store/" + str(key), data=payload,timeout=timeoutduration)
                    if fwdRequest.status_code == 200 or fwdRequest.status_code == 201:
                        break

                except req.exceptions.RequestException as ex:
                    print("   WARNING - Unable to reach replica address " + node + "! Exception Raised: ", ex)
                    deleteaddr(node)
            if fwdRequest != None:
                #PUT forward was successful
                print(fwdRequest.json())
                #The original receiving node should return the result of the forward
                return fwdRequest.json(), fwdRequest.status_code
            else:
                #PUT Forward failed
                return "Could not forward the PUT to the correct shard", 500

        print("(Log Message)[STORE] Initiating put!")

        #case of first put
        #other requests with an empty causal-metadata is illegal
        if meta == "":
            meta = causalmetadata

        print("meta: ", meta,flush=True)
        received_clock = meta.split(" ")
        print("received clock: ", received_clock, flush=True)
        current_clock = causalmetadata.split(" ")
        print("current_clock: ", current_clock, flush=True)
        print("replicaNumber: ", replicaNumber)

        #Soft copy of the vector clocks to work with
        currentCopy = current_clock.copy()
        receivedCopy = received_clock.copy()
        print("Current copy, received copy:", currentCopy, receivedCopy)
        updatedVC = checkVersion(currentCopy, receivedCopy, replicaNumber, key)
        print("updated VC", updatedVC)

        # set the current clock to the clock received from the other replica
        if updatedVC:
            current_clock = updatedVC

        # There was no conflict, set the indivdual replica clock
        clock = int(current_clock[replicaNumber])
        print("current node clock: ", clock)

        newclock = int(received_clock[replicaNumber])

        #make sure the nodes are not incrementing their own VC's, only the replica comminucating with the client should increment

        if not args["node"]:
            print("Update the clock")
            clock +=1
            print("incremented clock: ", clock)
            current_clock[replicaNumber] = str(clock)

        #then update point-wise max
        updated_clock = [max(value) for value in zip(current_clock, received_clock)]
        print("updated clock:", updated_clock,flush=True)

        causalmetadata = " ".join(updated_clock)

        #get the shard id of the given key
        shardid = getShardID(key,hr)
        print(shardid, flush=True)

        #if both shard id's match, continue with request normally. Otherwise, forward request to a node with the same shardid
        #as the key

        #incorrect input
        if not args["value"]:
            res = {'error':'Value is missing', 'message': 'Error in PUT'}
            return res,400

        #key is too long
        if len(key)>50:
            res = {'error':'Key is too long', 'message': 'Error in PUT'}
            return res,400

        # Confirmed to be a valid key! Create a newkey boolean and either add or replace.
        newkey = False
        if key not in dic:
            dic[key] = args["value"]
            res = {"message":"Added successfully", "causal-metadata": causalmetadata, "shard-id":shard_id}
            newkey = True
        else:
            # To prevent constant repetitive put calls between replicas...
            originalvalue = dic.get(key)
            if originalvalue == args["value"]:
                # Nothing changed. Do not broadcast.
                res = {"message":"Updated successfully", "causal-metadata": causalmetadata, "shard-id":shard_id}
                return res,200
            else:
                # Something changed!
                dic[key] = args["value"]
                res = {"message":"Updated successfully", "causal-metadata": causalmetadata, "shard-id":shard_id}
                newkey = False

        # Regardless of newkey, Broadcast the same PUT request to all other replicas in the view save for itself.
        valuevar = str(args["value"])
        payload = {"value" : valuevar, "causal-metadata": causalmetadata, "node":1}

        # only the the replica dealing with the client should broadcast the request
        if not args["node"]:
            print("  - Broadcasting PUT to other replicas...")
            print("Payload: ", payload)
            for replicaaddr in shard_members[shardid]:
                if replicaaddr != socketaddr:
                    print("  - Sending PUT to " + replicaaddr + "...")
                    try:
                        request = req.put("http://" + replicaaddr +"/key-value-store/"+str(key), data=payload, timeout=timeoutduration)
                        print("   - Success!")
                    except req.exceptions.RequestException as ex:
                        # WARNING, a replica in the view could not be reached! time to call key-value-store-view DELETE.
                        print("   WARNING - Unable to reach replica address " + replicaaddr + "! Exception Raised: ", ex)
                        deleteaddr(replicaaddr)
            # End Broadcast
            if newkey:
                return res,201
            else:
                return res,200

    def delete(self,key):
        print("(Log Message)[STORE] Initiating delete!")

        global causalmetadata
        global total_differences
        replicaNumber = getReplicaNumber()
        #replicaNumber is the index of the current replica
        #we can use this with the vector clock to figure out which one
        parser = reqparse.RequestParser(bundle_errors=True)
        parser.add_argument("causal-metadata", type=str)
        parser.add_argument("node", type=int)
        args = parser.parse_args()
        print(args, flush=True)

        #parsing meta-data
        meta = str(args["causal-metadata"])

        #catch wrong delete here
        shardid = getShardID(key, hr)
        if shardid != shard_id:
            # caught the wrong shard_id in PUT
            print("Caught wrong shardid, forwarding...", flush=True)
            nodelist = shard_members[shardid]
            valuevar = str(args["value"])
            payload = {"value": valuevar, "causal-metadata": meta}
            fwdRequest = None
            #try for all nodes in the shard, if one succeeds, break loop
            for node in nodelist:
                try:
                    print("Trying to forward DELETE to ", node)
                    fwdRequest = req.delete("http://" + node + "/key-value-store/" + str(key), data=payload,
                                         timeout=timeoutduration)
                    if fwdRequest.status_code == 200 or fwdRequest.status_code == 201:
                        break

                except:
                    print("   WARNING - Unable to reach replica address " + node + "! Exception Raised: ", ex)
                    deleteaddr(node)
            if fwdRequest != None:
                # PUT forward was successful
                print(fwdRequest.json())
                # The original receiving node should return the result of the forward
                return fwdRequest.json(), fwdRequest.status_code
            else:
                # PUT Forward failed
                return "Could not forward the DELETE to the correct shard", 500

        #case of first put
        #other requests with an empty causal-metadata is illegal
        if meta == "":
            meta = causalmetadata

        print("meta: ", meta,flush=True)
        received_clock = meta.split(" ")
        print("received clock: ", received_clock, flush=True)
        current_clock = causalmetadata.split(" ")
        print("current_clock: ", current_clock, flush=True)
        print("replicaNumber: ", replicaNumber)

        #Soft copy of the vector clocks to work with
        currentCopy = current_clock.copy()
        receivedCopy = received_clock.copy()
        print("Current copy, received copy:", currentCopy, receivedCopy)
        updatedVC = checkVersion(currentCopy, receivedCopy, replicaNumber, key)
        print("updated VC", updatedVC)

        # replica was behind, set the current clock to the clock received from the other replica
        if updatedVC:
            current_clock = updatedVC

        # There was no conflict, set the indivdual replica clock
        clock = int(current_clock[replicaNumber])

        newclock = int(received_clock[replicaNumber])

        #make sure the nodes are not incrementing their own VC's, only the replica comminucating with the client should increment
        if not args["node"]:
            clock +=1
            current_clock[replicaNumber] = str(clock)

        #then update point-wise max
        updated_clock = [max(value) for value in zip(current_clock, received_clock)]
        print("updated clock:", updated_clock,flush=True)

        causalmetadata = " ".join(updated_clock)
        shardid = getShardID(key,hr)
        print(shardid, flush=True)

        #key is too long
        if len(key)>50:
            res = {'error':'Key is too long', 'message': 'Error in DELETE'}
            return res,400

        payload = {"causal-metadata": causalmetadata, "node":1}
        if key in dic:
            del dic[key]

        # Broadcast same DELETE request to all other replicas in the view save for itself.
        if not args["node"]:
            print("  - Broadcasting DELETE to other replicas...")
            for replicaaddr in shard_members[shardid]:
                if replicaaddr != socketaddr:
                    print("  - Sending DELETE to " + replicaaddr + "...")
                    try:
                        #TO-DO change req.delete to a request("delete", URL, data = metadata, timeout=timeoutduration)
                        request = req.delete("http://" + replicaaddr + "/key-value-store/" + str(key), data = payload, timeout=timeoutduration)
                        print("   - Done!")
                    except Timeout as ex:
                        # WARNING, a replica in the view could not be reached! time to call key-value-store-view DELETE.
                        print("   WARNING - Unable to reach replica address " + replicaaddr + "! Exception Raised: ", ex)
                        deleteaddr(replicaaddr)

            return {"message":"Deleted successfully", "causal-metadata": causalmetadata, "shard-id":shard_id},200

        if key not in dic:
            return {'doesExist': False,'error':'Key does not exist','message':'Error in DELETE'},404

# /key-value-store-view
class ViewStore(Resource):
    def get(self):
        print("(Log Message)[VIEWSTORE] Initiating get!")
        viewstring = ','.join(viewstore)
        return {"message":"View retrieved successfully","view":viewstring}, 200

    def put(self):
        global causalmetadata
        global replicaNumber
        print("(Log Message)[VIEWSTORE] Initiating put!")
        parser = reqparse.RequestParser(bundle_errors=True)
        parser.add_argument("socket-address", type=str)
        args = parser.parse_args()

        targetsocket= args["socket-address"]
        if targetsocket in viewstore:
            return {"error":"Socket address already exists in the view","message":"Error in PUT"},404
        else:
            viewstore.append(targetsocket)
            viewstore.sort(key = sort_key)
            causaldata = causalmetadata.split(" ")
            print("causal data after adding new replica: ", causaldata, flush=True)
            if len(causaldata) < len(viewstore):
                print("There is more replicas than causaldata")
                causaldata.append("0")
                causalmetadata = " ".join(causaldata)
                print("new causalmetadata: ", causalmetadata, flush=True)
            return {"message":"Replica added successfully to the view"}, 201

    def delete(self):
        global causalmetadata
        global replicaNumber
        print("(Log Message)[VIEWSTORE] Initiating delete!")
        parser = reqparse.RequestParser(bundle_errors=True)
        parser.add_argument("socket-address", type=str)
        args = parser.parse_args()

        targetsocket= args["socket-address"]
        if targetsocket in viewstore:
            viewstore.remove(targetsocket)
            return {"message": "Replica deleted successfully from the view"}, 200

        return {"error":"Socket address does not exist in the view","message":"Error in DELETE"}, 404

# Used to get the KVS of a replica
class KVS(Resource):
    def get(self):
        return dic

class ShardIDs(Resource):
    #return all shard id's
    def get(self):
        return {"message":"Shard IDs retrieved successfully","shard-ids": getShards()},200

# Pretty self-explanatory - Returns any given node's shard_id.
class NodeShardId(Resource):
    def get(self):
        print("(Log Message)[SHARD] Initiating node-shard-id GET!")
        return {"message":"Shard ID of the node retrieved successfully","shard-id":shard_id}, 200

# Aux service not specified in the spec, for use in ShardIdMembers.
# For any node, return its shard_members.
class NodeShardMembers(Resource):
    def get(self):
        print("(Log Message)[SHARD] Initiating node-shard-members GET!")
        return {"message":"Shard Members of the node retrieved successfully","shard-id-members":shard_members}, 200

# Aux service not specified in spec, for use in ShardReshard.
# For any node, re-set it's shardID.
class NodeSetShardId(Resource):
    def put(self):
        #{"shard-id": id,"shards": shards, "shard-members":shard_members[id]}
        print("[NodeSetShardId] Initiating node-set-shard-id PUT!")
        parser = reqparse.RequestParser(bundle_errors=True)
        parser.add_argument("shard-id", type=str)
        parser.add_argument("shards", type=str)
        parser.add_argument("shard-members", type=str)
        args = parser.parse_args()
        newshardID = args["shard-id"] # get shard-id
        json_acceptable_string = args["shards"].replace("'", "\"")
        d = json.loads(json_acceptable_string)
        newshards = d # get shard-id
        json_acceptable_string = args["shard-members"].replace("'", "\"")
        d = json.loads(json_acceptable_string)
        newshard_members = d # get shard-id
        global shard_id
        global shards
        global shard_members
        print("[NodeSetShardId] Replacing current shard_id " + str(shard_id) + " with newshardID " + str(newshardID))
        shard_id = newshardID # update.
        print("[NodeSetShardId] Replacing current shards " + str(shards) + " with newshards " + str(newshards))
        shards = newshards # update.
        print("[NodeSetShardId] Replacing current shard_members" + str(shard_members) + " with newshard_members " + str(newshard_members))
        shard_members = newshard_members # update.
        print("[NodeSetShardId] Updating hashring...")
        shardcnt = len(shards)
        global hr
        hr = None
        hr = HashRing(hash_fn=m3h)
        #add the shards to the hashring
        if shardcnt != None:
            for i in range(int(shardcnt)):
                hr.add_node("shard"+str(i))
        print("[NodeSetShardId] Shards created are: ", getShards())

        return {"message":"Updated successfully"}, 200

# Aux service not specified in the spec, for use in ShardReshard.
# For any node, given the passed JSON object in the PUT call,
# overwrite the existing dictionary.
class KVSOverwite(Resource):
    def put(self):
        global dic
        print("(Log Message)[SHARD] Initiating KVSOverwrite PUT!")
        parser = reqparse.RequestParser(bundle_errors=True)
        parser.add_argument("kvs")
        args = parser.parse_args()
        json_acceptable_string = args["kvs"].replace("'", "\"")
        d = json.loads(json_acceptable_string)
        kvs = d # get kvs to overwrite existing dictionary.
        if kvs is None:
            kvs = {}
            print("Changing none payload to {}!")
        print("Overwiting existing dictionary:\n" + str(dic) + "\nwith passed kvs:\n" + str(kvs))
        dic = kvs
        return {"message":"Updated successfully"}, 200

# Given any shard-id, gets all of the members of that shard-id.
# Mechanism:
# = Check if current shard is the requested shard. if so, return shard_members.
# = Else, we have to find a node that is part of the shard. Iterate over all
# view socket addresses that are not present in shard_members, executing
# GET NodeShardId for each socket address until the answer matches with requested.
# = With that same address, execute GET NodeShardIdMembers (This is an aux service
# not specified in the spec.)
class ShardIdMembers(Resource):
    #URL Format: "/key-value-store-shard/shard-id-members/<shard-id>"
    def get(self, id):
        print("(Log Message)[SHARD] Initiating shard-id-members GET for id " + str(id))
        if id == shard_id:
            print("(Log Message)[SHARD] passed id is the same as host node's id!")
            print("Current shard_members:")
            print(shard_members)
            return {"message":"Members of shard ID retrieved successfully","shard-id-members":shard_members[id]}, 200
        else:
            print("(Log Message)[SHARD] passed id is NOT the same as host node's id, beginning search.")
            for replicaaddr in viewstore:
                if replicaaddr not in shard_members:
                    print("  - Sending GET to " + replicaaddr + "/key-value-store-shard/node-shard-id...")
                    try:
                        request = req.get("http://" + replicaaddr +"/key-value-store-shard/node-shard-id", timeout=timeoutduration)
                        print("   - Success! Got a response of " + str(request.text))
                        data = request.json()
                        if data['shard-id'] == id:
                            print('Got a matching shard-id!')
                            print("  - Sending GET to " + replicaaddr + "/key-value-store-shard/node-shard-members...")
                            # Found a hit! Get this address's shard members.
                            request = req.get("http://" + replicaaddr +"/key-value-store-shard/node-shard-members", timeout=timeoutduration)
                            print("   - Success! Got a response of " + str(request.text))
                            data = request.json()
                            # All done! Return it.
                            print("...(Log Message)[SHARD] shard-id-members GET is done!")
                            return {"message":"Members of shard ID retrieved successfully","shard-id-members":data['shard-id-members'][id]}, 200
                    except req.exceptions.RequestException as ex:
                        # WARNING, a replica in the view could not be reached! time to call key-value-store-view DELETE.
                        print("   WARNING - Unable to reach replica address " + replicaaddr + "! Exception Raised: ", ex)
                        deleteaddr(replicaaddr)
            print("ERROR - passed shard does not exist?!")
            return {"error":"Shard address does not exist!","message":"Error in GET"}, 404

class ShardKeyCount(Resource):
    #URL Format: "/key-value-store-shard/shard-id-key-count/<shard-id>"
    # return # of keys stored in the shard
    def get(self, id):
        numKeys=0
        for replicaaddr in shard_members[id]:
            #Making sure the replica is up
            if replicaaddr in viewstore:
                replica_dic = (req.get("http://" + replicaaddr +"/kvs", timeout=timeoutduration)).json()
                #print("replica dic:",replica_dic)
                for key in replica_dic:
                    #print("key",key)
                    numKeys+=1
                return {"message":"Key count of shard ID retrieved successfully","shard-id-key-count":numKeys},200

        #No replicas were up
        return {"No replicas up in the shard"},500

#TO-DO:
# This only puts to the specific node-socket-address receiving the PUT request
# Need to broadcast this put to the entire VIEW or only the shard
class ShardAddMember(Resource):
    #URL Format: "/key-value-store-shard/add-member/<shard-id>"
    # takes a PUT request and adds the socket-address to a shard
    def put(self, id):
        shardid = id
        parser = reqparse.RequestParser(bundle_errors=True)
        parser.add_argument("socket-address", type=str)
        args = parser.parse_args()

        targetsocket = args["socket-address"]
        if targetsocket not in viewstore:
            print("[WARNING]: socket address of the node is currently not in the view", flush=True)
        if shardid not in shards:
            return {"message":"ERROR: shard-id does not exist"}, 404
        if targetsocket not in shard_members[shardid]:
            shard_members[shardid].append(targetsocket)
            broadcastload = {"socket-address":targetsocket}
            for member in viewstore:
                if member != socketaddr and member != targetsocket:
                #broadcast to all shards
                    try:
                        addshardtomem = req.put("http://" + member + "/key-value-store-shard/add-member/"+shardid, data=broadcastload, timeout=5)
                    except:
                        print("could not add new member to ", member)
                        continue

            payload = {"shard-id":shardid, "socket-address": socketaddr}
            print("ShardAddMember payload: ", payload, flush=True)
            try:
                addshard = req.put("http://" + targetsocket +"/addtoshard", data=payload, timeout=5)
            except:
                print("Could not add to shard")
            return 200
        else:
            return {"message":"Node is already in the shard"}, 200

# Resharding class - to be excecuted by the administrator and not
# done automatically, in two cases: either 1) there is only 1
# node left in a shard or 2) new nodes have been added. Can
# be used to change the shard-count.

# Takes a PUT request, expecting JSON content {"shard-count":<shard-count>}.

# Mechanism design:
# = Collects all dictionaries from all different existing shards and gathers
# them in one dictionary.
# = Reshards using the existing assignshards() class
# = Redistributes keys
class ShardReshard(Resource):
    def put(self):
        global shard_id
        global shards
        print("[ShardReshard] Initiating reshard PUT!")
        parser = reqparse.RequestParser(bundle_errors=True)
        parser.add_argument("shard-count", type=str)
        args = parser.parse_args()
        shardcnt= args["shard-count"]
        numberOfNodes = len(viewstore)

        if int(shardcnt) > numberOfNodes or numberOfNodes // int(shardcnt) < 2:
            return {"message":"Illegal reshard"},400

        # **Collect all dictionaries from shards other than own shard (which node irrelevant)**

        # Create dictionary of all keys and values, initalized by current shard dictionary.
        aggregatedic = dic
        # With list of all shard ID's, shards, iterate.
        for id in shards:
            if id != shard_id: # If not current shard
                print("[ShardReshard] Querying dictionary for shard '" + str(id)+"'.")
                # For each shard, get list of all nodes in shard by calling same node's class function.
                request = ShardIdMembers.get(self, id)
                id_shard_members = request[0]['shard-id-members']
                print("[ShardReshard] Got id_shard_members:")
                print(id_shard_members)
                replicaaddr = id_shard_members[0] # It doesn't matter which address we go for in the shard.
                # Get all keys (KVS resource) from any node in the shard. Append to current dictionary.
                print("  - Sending GET to " + replicaaddr + "/kvs...")
                try:
                    request = req.get("http://" + replicaaddr +"/kvs", timeout=timeoutduration)
                    json_acceptable_string = request.text.replace("'", "\"")
                    d = json.loads(json_acceptable_string)
                    print("   - Success! Got a response of " + str(d))
                    # Append gotten key-value store to current dictionary
                    aggregatedic = {**aggregatedic, **d}
                    print("[ShardReshard] Dic of shard "+ str(id) + " added. Current combined dictionary: " + str(aggregatedic))
                except req.exceptions.RequestException as ex:
                    # WARNING, a replica in the view could not be reached! time to call key-value-store-view DELETE.
                    print("   WARNING - Unable to reach replica address " + replicaaddr + "! Exception Raised: ", ex)
                    deleteaddr(replicaaddr)
        print("[ShardReshard] Complete dictionary gathered!")

        # **Reshard existing shards.**

        # Copied code from initialization.
        # Reset current information for this node.
        shards = []
        shard_id = None
        shard_members = {}
        nodes = None

        if shardcnt != None:
            #Get node index from view list
            nodeidx = viewstore.index(socketaddr)
            #split the nodes in the viewlist into shards of index 0 to n
            nodes = assignShards(viewstore,shardcnt)
            #cannot partition nodes into shards if it cannot have at least 2 nodes per shard
            if nodes == -1:
                print("Error stated above")
            else:
                #range starts from 0
                for shard in range(int(shardcnt)):
                    if socketaddr in nodes[shard]:
                        shard_id = shard

        global hr
        hr = None
        hr = HashRing(hash_fn=m3h)

        #add the shards to the hashring
        if shardcnt != None:
            for i in range(int(shardcnt)):
                hr.add_node("shard"+str(i))
                shards.append("shard"+str(i))
        #assign all addresses to a shard
        #if socketaddr is part of the shard, then assign shardid to the node
        if nodes != None and shardcnt != None:
            for i in range(int(shardcnt)):
                shard_members["shard"+str(i)] = nodes[i]
                if socketaddr in shard_members["shard"+str(i)]:
                    shard_id = "shard"+str(i)
        #End copied code.
        print("[ShardReshard] Shards created are: ", getShards())
        print("[ShardReshard] Shard master list completed. New Schema now is: " + str(shard_members))# *****NOT COPIED

        # Apply effects to ALL other shards!
        for id in shards:
            payload = {"shard-id": str(id),"shards": str(shards), "shard-members": str(shard_members)}
            # For each shard, get list of all nodes in shard by calling same node's class function.
            for replicaaddr in shard_members[id]:
                print("  - Sending PUT to " + replicaaddr + "/key-value-store-shard/node-set-shard-id with payload "+str(payload)+"...")
                try:
                    request = req.put("http://" + replicaaddr +"/key-value-store-shard/node-set-shard-id", data=payload, timeout=timeoutduration)
                    print("   - Success!")
                except req.exceptions.RequestException as ex:
                    # WARNING, a replica in the view could not be reached! time to call key-value-store-view DELETE.
                    print("   WARNING - Unable to reach replica address " + replicaaddr + "! Exception Raised: ", ex)
                    deleteaddr(replicaaddr)
        print("[ShardReshard] All replicas have been updated to revised shard scheme!")

        # **Redistribute keys, overwriting existing.**

        # Requires SAME key-to-shard mechanism implemented in the PUT call for key-value-store.
        # Can copy that code down here and use it to create an array of dictionaries to apply to
        # each shard, and then call the auxiliary functions to overwrite and apply the devised
        # schema.
        sharddic = {} # master dictionary containing all dictionaries seprated by schema
        for id in shards:
            sharddic[id] = {} # Append an empty dictionary for each shard id.
        # Dictionary should now look something like: {'shard1':{},'shard2':{}}
        for key, value in aggregatedic.items(): # For each key/value pair
            shardID = getShardID(key, hr) # Apply the same schema as the PUT call
            sharddic[shardID][key] = value # Insert into dictionary for that shardID, ex) {'shard1':{'key':'value'},'shard2':{}}
        print("[ShardReshard] sharddic has been completed. It looks like: " + str(sharddic))

        for id in shards:
            print("[ShardReshard] For shardid " + str(id) + ", inserting kvs of " + str(sharddic[id]))
            payload = {"kvs": str(sharddic[id])}
            # For each shard, get list of all nodes in shard by calling same node's class function.
            for replicaaddr in shard_members[id]:
                print("  - Sending PUT to " + replicaaddr + "...")
                try:
                    request = req.put("http://" + replicaaddr +"/kvs/overwrite", data=payload, timeout=timeoutduration)
                    print("   - Success!")
                except req.exceptions.RequestException as ex:
                    # WARNING, a replica in the view could not be reached! time to call key-value-store-view DELETE.
                    print("   WARNING - Unable to reach replica address " + replicaaddr + "! Exception Raised: ", ex)
                    deleteaddr(replicaaddr)

        # All done!
        return {"message":"Resharding done successfully"}, 200

# Used to sync a replica in the case that it goes down and comes back online
def instantiateReplica(viewstore, socketaddress):
    missingTheReplica = []

    for address in viewstore:
        print("Working on: ", address)
        if address != socketaddress:
            print("trying to broadcast...")
            try:
                response = req.get("http://" + address + "/key-value-store-view", timeout=10)
                response = response.json()
                print("response: ", response)
                responseView = response['view']
                for views in responseView:
                    if socketaddress not in views:
                        if address not in missingTheReplica:
                            missingTheReplica.append(address)

            except req.exceptions.RequestException as ex:
                print("Could not get a response")
                print("It may be first run of the program OR something is terribly wrong")
    print("Replicas that need it: ", missingTheReplica)

    #To insert the replica's view to the ones that need it
    for addy in missingTheReplica:
        try:
            #Tries to send a request
            print("Sending put request....to: ", addy)
            payload = {"socket-address" : socketaddress}
            requestReplica = req.put("http://" + addy + "/key-value-store-view", data=payload, timeout=10)
            print("Reponse received from ", addy, " and response is: ", requestReplica.json())
        except req.exceptions.RequestException as xd:
            print("Could not send a PUT to ", addy)
            print("Boohoo :(")
    #now get the KVS data
    print("getting KVS from a replica")
    print("dictionary: ", dic, flush=True)
    if not dic:
        if shard_id != None:
            try:
                for address in shard_members[shard_id]:
                    if address != socketaddress:
                        kvs = req.get("http://" + address + "/kvs", timeout=5)
                        kvs = kvs.json()
                        print("Request response from kvs: ", kvs, flush=True)
                        if kvs:
                            print("adding dictionary from KVS to self")
                            for key in kvs:
                                dic[key] = kvs[key]
            except req.exceptions.RequestException as rex:
                print(rex, flush=True)
                print("Could not GET to the first replica in viewstore", flush=True)
        else:
            print("Not part of a shard yet, Did not get any KVS information", flush=True)
    print("SUCCESS!")

def getShards():
    return list(hr.get_nodes())

def getShardID(key, hashRing):
    return hashRing.get_node(key)

def assignShards(viewlist,shardCount):
    shardcount = int(shardCount)
    length = len(viewlist)
    if length // int(shardCount) == 1:
        print("Illegal sharding, must have at least two nodes per shard")
        return -1
    viewlist = np.array_split(viewlist,shardcount)
    print("view list split..: ", viewlist)
    for i in range(len(viewlist)):
        viewlist[i] = viewlist[i].tolist()
    print("array split into ", shardcount, "shards:")
    print(viewlist)
    print("\n\n\n")
    return viewlist

class addToShard(Resource):
    def put(self):
        global shard_id
        global shard_members
        parser = reqparse.RequestParser(bundle_errors=True)
        parser.add_argument("shard-id", type=str)
        parser.add_argument("socket-address", type=str)
        args = parser.parse_args()
        print(args, flush=True)
        if args['shard-id'] == None or args['socket-address'] == None:
            return "failed to add to shard", 500
        else:
            shard_id = args['shard-id']
            targetaddress = args['socket-address']
            members = req.get("http://"+targetaddress+"/getmembers", timeout=2)
            for member in members.json():
                shard_members[member] = members.json()[member]

            print("Nodes shard info: ", shard_members, flush=True)
            return "added to shard", 200

class getMembers(Resource):
    def get(self):
        return shard_members

if __name__ == "__main__":
    print("Hello there! CSE_Assignment4 replica instance initiated...")
    # Main instance.
    print(" Starting replica instance...")
    ip_add = "0.0.0.0"
    shardcnt = None
    #to ensure that the thread running has the required environment variables
    if 'SOCKET_ADDRESS' in os.environ and os.environ['SOCKET_ADDRESS'] != "":
        socketaddr = os.environ['SOCKET_ADDRESS']
        print("SOCKET_ADDRESS Docker env variable found! read: ", socketaddr)
    else:
        print("[ERROR] SOCKET_ADDRESS Docker env variable not found!")
    print("socket addr: ", socketaddr, flush=True)
    if 'VIEW' in os.environ and os.environ['VIEW'] != "":
        view = os.environ['VIEW']
        viewstore = view.split(",")
        viewstore.sort(key = sort_key)
        print("VIEW Docker env variable found! read: ", viewstore)
    else:
        print("[ERROR] VIEW Docker env variable not found!")
    #check to see if shard count is present. If not, we know that the node was not instantiated on startup
    if "SHARD_COUNT" in os.environ and os.environ["SHARD_COUNT"]!="":
        shardcnt = os.environ["SHARD_COUNT"]

    clock = []
    for addy in viewstore:
        clock.append("0")
    causalmetadata = " ".join(clock)

    nodes = None
    if shardcnt != None:
        #Get node index from view list
        nodeidx = viewstore.index(socketaddr)
        #split the nodes in the viewlist into shards of index 0 to n
        nodes = assignShards(viewstore,shardcnt)
        #cannot partition nodes into shards if it cannot have at least 2 nodes per shard
        if nodes == -1:
            print("Error stated above")
        else:
            #range starts from 0
            for shard in range(int(shardcnt)):
                if socketaddr in nodes[shard]:
                    shard_id = shard
    clock = []
    for addy in viewstore:
        clock.append("0")

    causalmetadata = " ".join(clock)
    # Main instance.
    print(" Starting replica instance...")
    ip_add = "0.0.0.0"

    hr = HashRing(hash_fn=m3h)

    #add the shards to the hashring
    if shardcnt != None:
        for i in range(int(shardcnt)):
            hr.add_node("shard"+str(i))
            shards.append("shard"+str(i))
    #assign all addresses to a shard
    #if socketaddr is part of the shard, then assign shardid to the node
    if nodes != None and shardcnt != None:
        for i in range(int(shardcnt)):
            shard_members["shard"+str(i)] = nodes[i]
            if socketaddr in shard_members["shard"+str(i)]:
                shard_id = "shard"+str(i)

    print("shards created are", getShards())
    instantiateReplica(viewstore, socketaddr)
    print("instatiating..")

    api.add_resource(Store, "/key-value-store/<key>")
    api.add_resource(ViewStore, "/key-value-store-view")
    api.add_resource(ShardIDs, "/key-value-store-shard/shard-ids")
    api.add_resource(NodeShardId, "/key-value-store-shard/node-shard-id")
    api.add_resource(ShardIdMembers, "/key-value-store-shard/shard-id-members/<id>")
    api.add_resource(ShardKeyCount, "/key-value-store-shard/shard-id-key-count/<id>")
    api.add_resource(ShardAddMember, "/key-value-store-shard/add-member/<id>")
    api.add_resource(ShardReshard, "/key-value-store-shard/reshard")
    # Aux resources
    api.add_resource(NodeShardMembers, "/key-value-store-shard/node-shard-members")
    api.add_resource(KVS, "/kvs")
    api.add_resource(KVSOverwite, "/kvs/overwrite")
    api.add_resource(NodeSetShardId, "/key-value-store-shard/node-set-shard-id")
    api.add_resource(addToShard, "/addtoshard")
    api.add_resource(getMembers, "/getmembers")
    app.run(host=ip_add,port=8085)


from keras.models import load_model
import cv2
import numpy as np
import sys

input = sys.argv[1]
output = sys.argv[2]

model = './u2netp_keras.h5'

# load model
u2netp_keras = load_model(model, compile=False)

# load image
image = cv2.imread(input)

# convert to array
blob = cv2.dnn.blobFromImage(image, 1.0/255, (320, 320), (0, 0, 0), swapRB=True, crop=False)

# predict
d1,d2,d3,d4,d5,d6,d7 = u2netp_keras.predict(blob)
pred = np.array(d1[:,0,:,:])[0]

# normalize
ma = np.max(pred)
mi = np.min(pred)
pred = (pred-mi)/(ma-mi)

pred = pred.squeeze()

pred = (pred*255).astype(np.uint8)

mask = cv2.resize(pred, image.shape[1::-1], interpolation=cv2.INTER_CUBIC)

# create alpha channel
b, g, r = cv2.split(image)
out = cv2.merge((b, g, r, mask))

# crop image
y, x = out[:,:,3].nonzero()
minx = np.min(x)
miny = np.min(y)
maxx = np.max(x)
maxy = np.max(y)
out = out[miny:maxy, minx:maxx]

# save image
cv2.imwrite(output, out)


import sys
import os
from datetime import datetime, timedelta
import csv
import logging
import string
import pdb
import re
import numpy as np
from netCDF4 import Dataset
from . import Time

"""Module: util.py

Methods:
    generate_date_range
    read_station_list
    read_station_table
    station_trunc
    equations_summary
"""

#NOTE: This was in Riley's util.py in mospred...I'm confused by it existing
#wouldn't this mean that station_cache is ALWAYS none whenever these
#functions are called?
station_cache = {'station_defs' : None, 'valid_stations' : None}

def generate_date_range(drlist):
    """Generates a list of dates in YYYYMMDDHH format. The format
    of the argument is 'YYYYMMDDHH-YYYYMMDDHH,stride'
    """

    if type(drlist) is str:
        drlist = [drlist]

    #Initialize the date list.
    dates = []
    for dr in drlist:
        if dr.isdigit(): #check for single date
            dates.append(Time.datetime_to_str(Time.str_to_datetime(dr)))
        else: #is range of dates
            dr_range = dr.split(",")[0]
            dr_stride = dr.split(",")[1]
            dt_start = Time.str_to_datetime(dr_range.split("-")[0])
            dt_stop = Time.str_to_datetime(dr_range.split("-")[1])
            if any(h in dr_stride for h in ['h','H']):
                delta = timedelta(hours=int(re.sub(r'[a-zA-Z]','',dr_stride)))
            #Stride from start to stop.  Note that stop time may not be in list.
            while dt_start <= dt_stop:
                dates.append(Time.datetime_to_str(dt_start))
                dt_start += delta

    return dates

def read_station_list(file_path):
    """Reads station IDs from file path defined from a
    control file. Returns them as a sorted list with
    no duplicates.
    """

    #Use previously saved station list if possible
    if station_cache['valid_stations'] is not None:
        return station_cache['valid_stations'].copy()

    #Read in station list from file provided and return
    #sorted list with no repeated stations
    station_list = []
    with open(file_path, 'r') as f:
        for line in f:
            station_list.append(line.strip())

    #Filter out undefined stations if possible
    if station_cache['station_defs'] is not None:
        stations_copy = station_list[:]
        for station in stations_copy:
            if station not in station_cache['station_defs']:
                station_list.remove(station)
    #Sort and remove duplicates
    valid_stations = list(sorted(set(station_list)))
    #Add to cache
    station_cache['valid_stations'] = valid_stations

    return valid_stations

def read_station_table(file_path,stalist):
    """Reads station metadata from the station table file.
    Reads all station definitions from file defined in control
    file. Returns a dictionary where the keys are the station
    call letters and dictionary values of form {lat: xxx, lon:yyy, ...}.
    Positive lat/lon values indicate North/West,
    while negative lat/lon values indicate South/East.
    """

    #Use station_cache data if available
    if station_cache['station_defs'] is not None:
        return station_cache['station_defs'].copy()

    #Create the station dictionary
    station_dict = {}
    with open(file_path,'r') as f:
        reader = csv.reader(f,delimiter=":",quoting=csv.QUOTE_NONE)
        for row in reader:
            call = row[0].strip(' ')
            #Only take the first definition for a call in the case of duplicates.
            if call not in station_dict:
                link1 = row[1]
                name = row[2]
                state = row[3]
                code = row[4]
                elev = row[5]
                lat = row[6]
                lon = row[7]
                tz = row[8]
                quality_flag = row[9]

                if lat[0] == 'N':
                    lat = float(lat[1:])
                elif lat[0] == 'S':
                    lat = -float(lat[1:])
                if lon[0] == 'W':
                    lon = -float(lon[1:])
                elif lon[0] == 'E':
                    lon = float(lon[1:])

                station_dict[call] = {}
                station_dict[call]['name'] = name
                station_dict[call]['long_name'] = name
                station_dict[call]['state'] = state
                station_dict[call]['code'] = code # Leave a string for now
                station_dict[call]['elev'] = int(elev) if len(elev.strip()) > 0 else 0
                station_dict[call]['lat'] = lat
                station_dict[call]['lon'] = lon
                station_dict[call]['tz'] = int(tz.strip()) if len(tz.strip()) > 0 else 0
                station_dict[call]['quality_flag'] = quality_flag

    #The station table has been read, now make sure each station
    #in the list is in the table.  Remove, if not.
    stalist_from_table = list(station_dict.keys())
    stalist_new = []
    for sta in stalist:
        if sta in stalist_from_table:
            stalist_new.append(sta)
        else:
            logging.warning("Station "+sta+" not found in table. Removing from list.")

    #Save in cache for later use
    station_cache['station_defs'] = station_dict

    return stalist_new,station_dict

def station_trunc(station_list):
    """Removes trailing '_' and ' ' from station names in given list."""

    station_list = [station.replace('_','').replace(' ','') for station in station_list if station is not None]

    return station_list

def equations_summary(**kwargs):
    """Writes out a summary of information from a regression equation, including:
    Reduction of Variance
    Standard Error
    Predictors used in the equation and their coefficients
    Equation Constant

    Usage:
    EITHER coefficients AND predictors (usage 1) OR filepath (usage 2) are required

    coefficients is an array or nested list of equation cofficients
    predictors is a string array of predictor names
    filepath is a string which points to an equations_driver output file

    other allowed keywords are:
    stations - a station name or list of station names (usage 1 and 2)

    predictands - a string array of predictand names (usage 1)

    variance - an array of Reduction of Variance (usage 1)

    error - an array of Standard Error Estimate (usage 1)

    consts - an array of Equation Constants (usage 1)

    outname - a filepath and filename to which the summary is written (usage 1 and 2)
    if outname is not provided, summary will be written to "equations_summary.txt"
    in the current working directory
    """

    # Get information from kwargs and open file to be written
    if "outname" not in kwargs:
        outname = "equations_summary.txt"
    else:
        outname = kwargs['outname']
    outfile = open(outname,"w")
    if "variance" in kwargs:
        variance = kwargs['variance']
    if "error" in kwargs:
        error = kwargs['error']
    if "consts" in kwargs:
        consts = kwargs['consts']
    if "averages" in kwargs:
        averages = kwargs['averages']
    if "stations" in kwargs:
        stations = kwargs['stations']
        if not isinstance(stations,list):
            stations = [stations] #Set stations to list if it isn't already
    #First use case: coefficients and predictors are provided.
    #Output will be created directly from these input
    if "coefficients" in kwargs and "predictors" in kwargs:
        coefficients = kwargs['coefficients']
        predictors = kwargs['predictors']
        #Loop over input stations
        for n,sta in enumerate(stations):
            if isinstance(sta,list): sta = sta[0]
            if isinstance(sta,bytes): sta = sta.decode()
            if isinstance(coefficients,list): coefficients = np.array(coefficients)
            #Find indices coefficients for station which are nonzero
            #and find the associated predictor names
            #If predictands was provided, the output will name the predictand,
            #otherwise a numeral will be provided
            if "predictands" in kwargs:
                predictands = kwargs['predictands']
                for m,tand in enumerate(predictands):
                    coefs_ind = np.nonzero(coefficients[n,:,m])[0]
                    preds = predictors[coefs_ind,:]
                    coefs = coefficients[n,coefs_ind,m]
                    try:
                        avg = averages[n,m]
                    except NameError:
                        avg = None
                    try:
                        var = variance[n,m]
                    except NameError:
                        var = None
                    try:
                        err = error[n,m]
                    except NameError:
                        err = None
                    try:
                        const = consts[n,m]
                    except NameError:
                        const = None
                    pred_strings = [pred.tostring() for pred in preds]
                    # Make sure tand is byte strings, convert to str and combine characters in array
                    if isinstance(tand[0],bytes): tand = (tand.tostring()).decode()
                    if pred_strings:
                        # Construct an output string based on all input information
                        info_string1 = "Equation Summary for station: {sta} for predictand: {tand}\n".format(sta=sta,tand=tand)
                        if avg:
                            info_string1 += "Predictand Average: "+str(avg)+"\n"
                        if var:
                            info_string1 += "Variance of Reduction: "+str(var)+" "
                        if err:
                            info_string1 += "Standard Error Estimate: "+str(err)+"\n"
                        if var and not err:
                            info_string1 += "\n"
                        info_string2=''
                        for i,pred in enumerate(pred_strings): info_string2+=str(pred.decode())+","+str(coefs[i])+"\n"
                        if const:
                            info_string2+="Equation Constant: "+str(const)+"\n"
                        info_string = info_string1+info_string2+"\n"
                        outfile.write(info_string)
                    elif not pred_strings:
                        info_string = "No equation information available for station: "+sta+" for predictand: "+tand+"\n\n"
                        outfile.write(info_string)
                outfile.write("\n")
            # If no predictand list is provided, denote predictand with a numeral
            else:
                for m in range(coefficients[n].shape[1]):
                    coefs_ind = np.nonzero(coefficients[n,:,m])[0]
                    preds = predictors[coefs_ind,:]
                    coefs = coefficients[n,coefs_ind,m]
                    try:
                        avg = averages[n,m]
                    except NameError:
                        avg = None
                    try:
                        var = variance[n,m]
                    except NameError:
                        var = None
                    try:
                        err = error[n,m]
                    except NameError:
                        err = None
                    try:
                        const = consts[n,m]
                    except NameError:
                        const = None
                    pred_strings = [pred.tostring() for pred in preds]
                    if pred_strings:
                        # Construct an output string based on all input information
                        info_string1 = "Equation Summary for station: {sta} for predictand #{tand_n}:\n".format(sta=sta,tand_n=str(m+1))
                        if avg:
                            info_string1 += "Predictand Average: "+str(avg)+"\n"
                        if var:
                            info_string1 += "Variance of Reduction: "+str(var)+" "
                        if err:
                            info_string1 += "Standard Error Estimate: "+str(err)+"\n"
                        if var and not err:
                            info_string1 += "\n"
                        info_string2=''
                        for i,pred in enumerate(pred_strings): info_string2+=str(pred)+","+str(coefs[i])+"\n"
                        if const:
                            info_string2+="Equation Constant: "+str(const)+"\n"
                        info_string = info_string1+info_string2+"\n"
                        outfile.write(info_string)
                    elif not pred_strings:
                        outfile.write("No equation information available for station: "+sta+" for predictand #"+str(m+1)+".\n\n")
                outfile.write("\n")
        outfile.close()
    #Second use case: a path to a file is provided.
    #Necessary equation information will be drawn from file.
    #If a station or list of stations is not provided, will run for all stations in file.
    elif "filepath" in kwargs:
        filepath = kwargs['filepath']
        pred_file = Dataset(filepath)
        if "stations" not in kwargs:
            file_stations = station_trunc([sta.tostring() for sta in pred_file.variables['station'][:]])
            stations = file_stations

        coefficients = pred_file.variables['MOS_Equations'][:,:-1,:]
        consts = pred_file.variables['MOS_Equations'][:,-1,:]
        variance = pred_file.variables['Reduction_of_Variance'][:]
        error = pred_file.variables['Standard_Error_Estimate'][:]
        averages = pred_file.variables['Predictand_Average'][:]
        predictors = pred_file.variables['Equations_List'][:-1,:]
        predictands = pred_file.variables['Predictand_List'][:]
        for n,sta in enumerate(stations):
            if isinstance(sta,list): sta = sta[0]
            #Find indices coefficients for station which are nonzero
            #and find the associated predictor names
            for m,tand in enumerate(predictands):
                coefs_ind = np.nonzero(coefficients[n,:,m])[0]
                preds = predictors[coefs_ind,:]
                coefs = coefficients[n,coefs_ind,m]
                var = variance[n,m]
                err = error[n,m]
                const = consts[n,m]
                avg = averages[n,m]
                pred_strings = [pred.tostring() for pred in preds]
                if pred_strings:
                    # Contstruct output string based on all input information
                    info_string1 = "Equation Summary for station: {sta} for predictand: {tand}\n".format(sta=sta,tand=tand.tostring())
                    info_string1 += "Predictand Average: "+str(avg)+"\n"
                    info_string1 += "Variance of Reduction: "+str(var)+" "
                    info_string1 += "Standard Error Estimate: "+str(err)+"\n"
                    info_string2=''
                    for i,pred in enumerate(pred_strings): info_string2+=str(pred)+","+str(coefs[i])+"\n"
                    info_string2+="Equation Constant: "+str(const)+"\n"
                    info_string = info_string1+info_string2+"\n"
                    outfile.write(info_string)
                elif not pred_strings:
                    outfile.write("No equation information available for station: "+sta+" for predictand: "+tand.tostring()+"\n\n")
            outfile.write("\n")
        outfile.close()
    # Raise error if neither first nor second use case are met
    else:
        raise ValueError("Either coefficients and predictors arguments OR filepath argument must be given")


class Solution:
    def subsetXORSum(self, nums: List[int]) -> int:

        def getXOR(ar):
            res = 0
            for i in ar: res ^= i
            return res

        ans = 0
        for r in range(1, len(nums)+1):
            for com in combinations(nums, r):
                ans += getXOR(com)

        return ans


import uuid, datetime
import logging

from orderapp.main.model.order_dto import OrderDto
from orderapp.main.model.order_model import Order
from orderapp.main.model.product_model import Product
from orderapp.main.model.product_catalog_model import ProductCatalog
from orderapp.main.model.offer_model import Offer

from orderapp.db import db

from sqlalchemy.exc import SQLAlchemyError

#Logging configurations
logging.basicConfig(level=logging.DEBUG)

product = OrderDto.product

def save_order(order_request):
    order = lambda: None
    try:
        if order_request != None:
            order_id = str(uuid.uuid4())
            products = []
            product_request = order_request['products']

            if  product_request == None:
                raise Exception
                logging.error("Products not included in request", exc_info=True)

            catalogs =  get_all_product_catalog()
            for product in product_request:
                product_type=product['producttype']
                for catalog in catalogs:
                    if catalog.producttype == product_type:
                        product = Product(
                            producttype=product_type,
                            units=product['units'],
                            discountedunits=calculate_total_product_units_offer(requested_product_unit=product['units'],catalog_id=catalog.catalog_id),
                            order_id=order_id,
                            catalog_id=catalog.catalog_id
                        )
                        products.append(product)

            order = Order(
                username=order_request['username'],
                orderid=order_id,
                orderdate=datetime.datetime.utcnow(),
                totalordercost=round(calculate_total_order_cost(products),2),
                products=products
            )
            save_changes(order)
        else:
            order = None
    except KeyError as exp:
        raise KeyError
        logging.error("Key missing", exc_info=True)
    except SQLAlchemyError as exp:
        raise SQLAlchemyError
        logging.error("DB Error", exc_info=True)
    except Exception as exp:
        logging.error("Error occured while saving the order", exc_info=True)
    return order

def calculate_total_order_cost(products):
    try:
        order_cost=0
        if products!=None:
            for product in products:
                catalog = get_product_catalog_for_id(product.catalog_id)
                order_cost = order_cost + catalog.cost*product.units
                logging.info(order_cost)
    except AttributeError as exp:
        raise AttributeError
        logging.error("Attribute missing", exc_info=True)
    except Exception as exp:
        logging.error("Error occured while calculating the total cost", exc_info=True)
    return order_cost

#Calculates the total units to be added in the basket after applying the offers
def calculate_total_product_units_offer(requested_product_unit, catalog_id):
    number_of_products=0
    try:
        logging.info(requested_product_unit)
        if requested_product_unit != None and catalog_id != None:
            offer = get_offer_for_catalog(catalog_id)
            number_of_products = requested_product_unit + (requested_product_unit*offer.discount_fractional_value_on_unit)
    except AttributeError as exp:
        raise AttributeError
        logging.error("Attribute missing", exc_info=True)
    except Exception as exp:
        logging.error("Error occured while calculating the total cost", exc_info=True)
    return number_of_products

def save_changes(data):
    db.session.add(data)
    db.session.commit()

def get_orders():
    return Order.query.all()

def get_a_order(order_id):
    return Order.query.filter_by(orderid=order_id).first()

def get_all_product_catalog():
    return ProductCatalog.query.all()

def get_product_catalog_for_id(catalog_id):
    return ProductCatalog.query.filter_by(catalog_id=catalog_id).first()

def get_offer_for_catalog(catalog_id):
    return Offer.query.filter_by(catalog_id=catalog_id).first()


# from pudb import set_trace; set_trace()
from typing import List
from collections import defaultdict
import math
import heapq

class Solution:
    def minimumWeight(self, n: int, edges: List[List[int]], src1: int, src2: int, dest: int) -> int:
        """Can't solve this, because my dumbass didn't realize that finding
        the path with the min weight is Dijkstra. Below I will reproduce the
        popular solution of three Dijkstras (ref: https://leetcode.com/problems/minimum-weighted-subgraph-with-the-required-paths/discuss/1844130/Python-3-Dijkstras-explained.)

        However, this solution (https://leetcode.com/problems/minimum-weighted-subgraph-with-the-required-paths/discuss/1844479/Simultaneous-Dijkstra-beats-100-(only-1-dijkstra))
        uses only one Dijkstra.

        O(ElogV + N) as according to Wikipedia. 3249 ms, 71% ranking.
        """
        # first produce two graphs, one in the original order, and the other
        # reversed order
        G1, G2 = defaultdict(list), defaultdict(list)
        for f, t, w in edges:
            G1[f].append((t, w))
            G2[t].append((f, w))  # reverse

        def Dijkstra(graph, start) -> List[int]:
            queue, weights = [(0, start)], {}
            while queue:
                w, node = heapq.heappop(queue)
                if node not in weights:  # weights always record the min weight
                    weights[node] = w
                    for child, weight in graph.get(node, []):
                        heapq.heappush(queue, (w + weight, child))
            return [weights.get(i, math.inf) for i in range(n)]

        s1toall = Dijkstra(G1, src1)
        s2toall = Dijkstra(G1, src2)
        dstoall = Dijkstra(G2, dest)

        res = min(s1toall[i] + s2toall[i] + dstoall[i] for i in range(n))
        return res if res < math.inf else -1

sol = Solution()
tests = [
    (6, [[0,2,2],[0,5,6],[1,0,3],[1,4,5],[2,1,1],[2,3,3],[2,3,4],[3,4,2],[4,5,1]], 0, 1, 5, 9),
    (3, [[0,1,1],[2,1,1]], 0, 1, 2, -1),
]

for i, (n, edges, src1, src2, dest, ans) in enumerate(tests):
    res = sol.minimumWeight(n, edges, src1, src2, dest)
    if res == ans:
        print(f'Test {i}: PASS')
    else:
        print(f'Test {i}; Fail. Ans: {ans}, Res: {res}')


from rdkit import Chem

import os.path as osp
from statistics import mode, StatisticsError
import numpy as np
import pandas as pd
import pickle

# location of unzipped GEOM dataset
true_confs_dir = '../data/DRUGS/drugs'

def clean_confs(smi, confs):
    good_ids = []
    smi = Chem.MolToSmiles(Chem.MolFromSmiles(smi), isomericSmiles=False)
    for i, c in enumerate(confs):
        conf_smi = Chem.MolToSmiles(Chem.RemoveHs(c), isomericSmiles=False)
        if conf_smi == smi:
            good_ids.append(i)
    return [confs[i] for i in good_ids]

def correct_smiles(true_confs):

    conf_smis = []
    for c in true_confs:
        conf_smi = Chem.MolToSmiles(Chem.RemoveHs(c))
        conf_smis.append(conf_smi)

    try:
        common_smi = mode(conf_smis)
    except StatisticsError:
        return None  # these should be cleaned by hand

    if np.sum([common_smi == smi for smi in conf_smis]) == len(conf_smis):
        return mode(conf_smis)
    else:
        print('consensus', common_smi)  # these should probably also be investigated manually
        return common_smi
        # return None

test_data = pd.read_csv('../data/DRUGS/test_smiles_raw.csv')
corrected_smiles_dict = {}
failed_smiles = []
for i, data in test_data.iterrows():

    smi = data.smiles

    try:
        with open(osp.join(true_confs_dir, smi.replace('/', '_') + '.pickle'), "rb") as f:
            mol_dic = pickle.load(f)
    except FileNotFoundError:
        print(f'cannot find ground truth conformer file: {smi}')
        continue

    true_confs = [conf['rd_mol'] for conf in mol_dic['conformers']]
    true_confs = clean_confs(smi, true_confs)
    if len(true_confs) == 0:
        corrected_smiles_dict[smi] = smi
        continue

    corrected_smi = correct_smiles(true_confs)
    corrected_smiles_dict[smi] = corrected_smi

    if corrected_smi is None:
        failed_smiles.append(smi)
        corrected_smiles_dict[smi] = smi
        print(f'failed: {smi}\n')

test_data['corrected_smiles'] = list(corrected_smiles_dict.values())
test_data.to_csv('../data/DRUGS/test_smiles_corrected.csv', index=False)


p = 'yes'
dd = {}
while p != 'no':
    name = input('\nWhat is your name?\n')
    go = input("\nWhat's your dream destination?\n")
    dd[name] = go
    p = input('\nIs there anyone else who wants to take the poll?(yes/no)\n')

for name, go in dd.items():
    print('\n' + name.title() + " would like to go to " + go.title() + '.')


# from scipy.sparse.csgraph import floyd_warshall
INF = 100000000

def make_adj_list(vertex, edge):
    adj_list = []
    dp = []

    # adj_listの初期化と入力
    for i in range(vertex):
        adj_list.append([])
        for j in range(vertex):
            if i == j:
                adj_list[i].append(0)
            else:
                adj_list[i].append(INF)
    for i in range(edge):
        s, t, cost = map(int, input().split())
        adj_list[s][t] = cost

    # dpの初期化
    for i in range(1 << vertex):
        dp.append([])
        for j in range(vertex):
            dp[i].append(-1)

    return adj_list, dp

def cal_min_cost(dp, vertex, edge, d, already, now_v):
    if dp[already][now_v] >= 0:
        # すでに通ったことがある場合
        return dp[already][now_v]

    if already == (1 << vertex) - 1 and now_v == 0:
        # 全ての頂点を通った
        dp[already][now_v] = 0
        return dp[already][now_v]

    res = INF
    for i in range(vertex):
        if not already >> i & 1:
            # 次にiに移動する
            res = min(res, cal_min_cost(dp, vertex, edge, d,
                      already | 1 << i, i) + d[now_v][i])
    dp[already][now_v] = res
    return dp[already][now_v]

def main():
    vertex, edge = map(int, input().split())
    adj_list, dp = make_adj_list(vertex, edge)  # 初期化と入力
    # d = floyd_warshall(adj_list)  # それぞれの頂点からの最小を求める
    d = adj_list
    # for k in range(vertex):
    #     for i in range(vertex):
    #         for j in range(vertex):
    #             d[i][j] = min(d[i][j], d[i][k] + d[k][j])
    # print(d)
    # 最短距離は頂点のどこからはじめても変わらないので0からスタートとする
    min_cost = cal_min_cost(dp, vertex, edge, d, 0, 0)  # 計算
    if min_cost == INF:
        print(-1)
    else:
        print(int(min_cost))

if __name__ == "__main__":
    main()


# -*- coding:utf-8 -*-
import subprocess
import hashlib
import os
import re
import socket
import shlex
import xml.etree.ElementTree as ET

import requests
import json
import time
import telnetlib
from pysqlcipher3 import dbapi2 as sqlite

def check_devices():
    cmd = 'adb devices'
    args = shlex.split(cmd)
    ret = subprocess.call(args)
    return ret

def check_root():
    cmd = 'adb root'
    args = shlex.split(cmd)
    info = subprocess.call(args)
    return info

def get_uin():
    return '-1763681588'
    cmd = 'adb shell su 0 cat /data/data/com.tencent.mm/shared_prefs/system_config_prefs.xml'
    args = shlex.split(cmd)
    info = subprocess.check_output(args).decode('utf-8')

    print(type(info))
    print(GetMiddleStr(info,'xml ',''))
    exit()
    print(re.sub('enter main '+GetMiddleStr(xml_str,'<?xml','>')+" /dev/null",'',xml_str))
    exit(12)
    root = ET.fromstring(xml_str)
    for child in root:
        if child.attrib['name'] == 'default_uin':
            uin = child.attrib['value']
            break
    if uin == "0":
        print("当前手机未登录微信,请登录")
        time.sleep(3)
        win32gui.EnumWindows(handle_window, None)
    else:
        return uin

def GetMiddleStr(content,startStr,endStr):
    patternStr = r'%s(.+?)%s'%(startStr,endStr)
    p = re.compile(patternStr,re.IGNORECASE)
    m= re.match(p,content)
    if m:
        return m.group(1)

def get_imeia():
    imei = subprocess.getoutput("cd ./AdbLib/&adb shell service call iphonesubinfo 1")
    pattern = re.compile("'.*'")
    res = pattern.findall(imei)
    if len(res) > 0:
        rawImei = ''
        for i in res:
            rawImei += i
        imei = rawImei.replace('.', '').replace("'", '').replace(' ', '')
    if len(imei) == 0:
        print("imei 输入错误")
        exit(255)
    return imei

def get_imeib():
    try:
        cmd = './AdbLib/adb.exe shell su 0 cat /data/data/com.tencent.mm/shared_prefs/DENGTA_META.xml'
        args = shlex.split(cmd)
        info = subprocess.check_output(args)
        root = ET.fromstring(info)
        for child in root:
            if child.attrib['name'] == 'IMEI_DENGTA':
                return child.text
    except:
        imei= get_imeia()
        return imei

def get_key(uin, imei):
    raw = imei + uin
    return hashlib.md5(str(raw).encode('utf8')).hexdigest()[0:7]

def db_file(uin,fileName):
    path = hashlib.md5(str('mm' + uin).encode('utf8')).hexdigest()
    cmd = './AdbLib/adb.exe shell su 0 cp /data/data/com.tencent.mm/MicroMsg/' + \
          path + '/EnMicroMsg.db /sdcard/' + fileName
    args = shlex.split(cmd)
    suc = subprocess.call(args)
    if suc == 0:
        cmd = './AdbLib/adb.exe pull /sdcard/' + fileName + ' ./' + fileName
        args = shlex.split(cmd)
        ret = subprocess.call(args)
        if ret != 0:
            print("获取加密数据库失败,请联系技术处理")

def send_file(filepath,ip,file_name):
    url = 'http://'+ip+'/download.php'
    # 要上传的文件
    files = {'file': (file_name+".db", open(filepath, 'rb'))
             }  # 显式的设置文件名
    # post携带的数据
    data = {'file_name': file_name}
    r = requests.post(url, files=files, data=data)
    return json.loads(r.text).get('code')

def handle_window(hwnd, extra):
    if win32gui.IsWindowVisible(hwnd):
        if 'cmd' in win32gui.GetWindowText(hwnd):
            win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0)
def cp_file(uin, fileName):

    print("正在上传文件,请稍等,不要关闭窗口")
    path = hashlib.md5(str('mm' + uin).encode('utf8')).hexdigest()
    cmd = 'adb shell su 0 cp /data/data/com.tencent.mm/MicroMsg/' + \
          path + '/EnMicroMsg.db /sdcard/' + fileName
    args = shlex.split(cmd)
    suc = subprocess.call(args)
    if suc == 0:
        cmd = 'adb pull /sdcard/' + fileName + ' ./' + fileName
        args = shlex.split(cmd)
        ret = subprocess.call(args)
        if ret != 0:
            exit(255)
    else:
        print("拷贝文件失败")

def send_data(url, data):
    r = requests.post(url, data)
    return r

def decrypt(key, dbName,decodeDbName):

    if os.path.isfile(decodeDbName):
        os.unlink(decodeDbName)
    conn = sqlite.connect(dbName)
    c = conn.cursor()
    try:
        c.execute("PRAGMA key = '" + key + "';")
        c.execute("PRAGMA cipher_use_hmac = OFF;")
        c.execute("PRAGMA cipher_page_size = 1024;")
        c.execute("PRAGMA kdf_iter = 4000;")
        c.execute("ATTACH DATABASE '" + decodeDbName + "' AS db KEY '';")
        c.execute("SELECT sqlcipher_export('db');")
        c.execute("DETACH DATABASE db;")
        c.close()
        status = 1
    except Exception as e:
        c.close()
        status = 0
    return status

def getKey(uin, imei):
    raw = imei+uin
    return hashlib.md5(str(raw).encode('utf8')).hexdigest()[0:7]

def query(dbName, sql):
    conn = sqlite.connect(dbName)
    c = conn.cursor()
    try:
        count = c.execute(sql)
        for raw in count:
            return raw[0]
        c.close()
    except Exception as e:
        c.close()

if __name__ == "__main__":

    if(check_devices()!=0):
        print("当前电脑没有连接手机,请重连手机,再次尝试(本窗口将自动关闭)")

        exit("设备未连接")
    if(check_root()!=0):
        print("当前设备未获取root权限,请联系技术(本窗口将自动关闭)")
        exit("设备未root")
    # data = input("是否上传(Y/N)：")
    # if data=="N":
    #     print("取消上传,即将关闭窗口")
    #     time.sleep(2)
    #     win32gui.EnumWindows(handle_window, None)

    data = "Y"
    if data=="Y":
        # ip =check_ip()
        # if ip==None:
        #     print("无可用服务器,请立即联系技术")
        #     exit(400)
        uin = get_uin()
        imei = get_imeib();
        key = getKey(uin, imei)

        file_name = str(time.strftime("%y%m%d%H%M%S", time.localtime()))+str(imei)+str(uin)
        decodeDbName = 'decrypted-' + file_name + ".db"
        cp_file(uin,file_name+".db")
        print(key)
        decrypt(key, file_name, decodeDbName)

        sql = "SELECT COUNT(*) FROM main.rcontact WHERE type = '3' AND verifyFlag = '0';"
        print(query(decodeDbName,sql))


'''
Part 1 - Python script to allocate numpy array in GPU.

Based on Neil Henning's following blog post, describing sample C++ code for
GPU computation using (low level) Vulkan API.

http://www.duskborn.com/posts/a-simple-vulkan-compute-example/

'''
import ctypes
import random
import numpy as np
from vulkan import *
from vulkan._vulkancache import ffi

# ************************************************************************************************
# Boiler plate code to select appropiate GPU device, create logical device, compute queue,
# allocate memory for data, etc.

appInfo = VkApplicationInfo(sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
                            pApplicationName = "Allocate array in GPU",
                            applicationVersion=VK_MAKE_VERSION(1, 0, 0),
                            pEngineName = "Gyan Basyal",
                            engineVersion = VK_MAKE_VERSION(1, 0, 0),
                            apiVersion = VK_API_VERSION_1_0
                            )

createInfo = VkInstanceCreateInfo(sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
                                 flags = 0,
                                 pApplicationInfo = appInfo,
                                 )

instance = vkCreateInstance(createInfo, None)

# query physical devices
physical_devices = vkEnumeratePhysicalDevices(instance)

physical_devices_features = [vkGetPhysicalDeviceFeatures(physical_device) for physical_device in physical_devices]

physical_devices_properties = [vkGetPhysicalDeviceProperties(physical_device) for physical_device in physical_devices]

# select the first device in the list
physical_device = physical_devices[0]

queue_families = vkGetPhysicalDeviceQueueFamilyProperties(physicalDevice=physical_device)
queue_family_compute_index = -1

# find unit within the device that supports computation operation
for i, queue_family in enumerate(queue_families):
    if (queue_family.queueCount > 0 and queue_family.queueFlags & VK_QUEUE_COMPUTE_BIT):
        queue_family_compute_index = i
        # found it!
        break

device_queue_create = [VkDeviceQueueCreateInfo(sType=VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
                                               queueFamilyIndex = queue_family_compute_index,
                                               queueCount = 1,
                                               pQueuePriorities = [1.0],
                                               flags = 0
                                               )]

device_create = VkDeviceCreateInfo(sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
                                   pQueueCreateInfos = device_queue_create,
                                   queueCreateInfoCount = len(device_queue_create),
                                   pEnabledFeatures = physical_devices_features[0],
                                   flags= 0
                                   )

# create logical device in the physical device
logical_device = vkCreateDevice(physical_device, device_create, None)

# Allocate 2*4*16384 bytes (_ppData) in gpu. The memory is divided equally between
# two buffers (in_buffer and out_buffer) in Part 2.

buffer_length = 16384
buffer_size = buffer_length * ctypes.sizeof(ctypes.c_int)
mem_size = buffer_size * 2
mem_props = vkGetPhysicalDeviceMemoryProperties(physical_device)
mem_type_index = -1
for k in range(mem_props.memoryTypeCount):
    if  ((VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT & mem_props.memoryTypes[k].propertyFlags) and
        (VK_MEMORY_PROPERTY_HOST_COHERENT_BIT & mem_props.memoryTypes[k].propertyFlags) and
        (mem_size < mem_props.memoryHeaps[mem_props.memoryTypes[k].heapIndex].size)):
        mem_type_index = k
        break

if mem_type_index == -1:
    print('Memory allocation error')
    sys.exit(1)
elif mem_type_index == VK_MAX_MEMORY_TYPES:
    print('Insufficient Host Memory')
    sys.exit(1)

memAllocInfo = VkMemoryAllocateInfo(sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
                                    allocationSize = mem_size,
                                    memoryTypeIndex = mem_type_index
                                    )
device_memory = vkAllocateMemory(logical_device,memAllocInfo,0)

_ppData = vkMapMemory(logical_device,device_memory,0,mem_size,0)

# ************************************************************************************************
# Initialize the memory with random integer (cffi approach) from CPU

ppData = ffi.from_buffer("int[]",_ppData)
print('** Assign random int between -1000 and 1000')
for x in range(buffer_length):
    ppData[x] = random.randint(-1000,1000)

# ************************************************************************************************
# Further modify gpu memory (using numpy) from CPU.

arr = np.frombuffer(_ppData,dtype=np.int32)
print('** Numpy array:')
print(arr)

arr[2] = 100
print('** Buffer after assigning 100 to index 2:')
print('Numpy array: %r'%arr)
print(ppData[2])
if not arr[2] == ppData[2]:
    print('Error')

arr[:] = 2000
print('** Buffer after assigning 2000 to each element')
print('Numpy array: %r'%arr)
print(ppData[2])

arr[:buffer_length] = 4000
arr[buffer_length:] = 0


"""005 enrollments

Revision ID: 9c54ab0a43c5
Revises: 6d8dac873e40
Create Date: 2020-03-17 17:37:40.801390

"""
from alembic import op
import sqlalchemy as sa

# revision identifiers, used by Alembic.
revision = '9c54ab0a43c5'
down_revision = '6d8dac873e40'
branch_labels = None
depends_on = None

def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table('enrollments',
    sa.Column('event_id', sa.Integer(), nullable=False),
    sa.Column('participant_id', sa.Integer(), nullable=False),
    sa.Column('timestamp', sa.DateTime(), nullable=False),
    sa.ForeignKeyConstraint(['event_id'], ['events.id'], ),
    sa.ForeignKeyConstraint(['participant_id'], ['participants.id'], ),
    sa.PrimaryKeyConstraint('event_id', 'participant_id')
    )
    # ### end Alembic commands ###

def downgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table('enrollments')
    # ### end Alembic commands ###


# -*- coding: utf-8 -*-
"""
Created on Wed Jul 19 23:38:39 2023

@author: Shashank.S.Tiwari
"""

import os
import glob
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns

# Set the folder path where the CSV files are located
folder_path = 'C:/Users/Shashank.S.Tiwari/OneDrive - Shell/Simulations/SUF_PearlGTL/40per_800TPH/16ppm_900secs/'

# Get a list of all CSV files in the folder
csv_files = glob.glob(os.path.join(folder_path, '*.out'))

# Set the DPI value for the plot (adjust as needed)
dpi = 300

# Set font options
font_size = 14
font_family = 'Arial'

# Set color palette
color_palette = 'viridis'  # Choose a color palette of your preference

# Set seaborn style
sns.set(style='ticks')

# Loop through each CSV file
for file in csv_files:
    # Read the CSV file and skip the first three lines
    df = pd.read_csv(file, delimiter=' ', skiprows=3, header=None)

    # Check the number of columns in the DataFrame
    if df.shape[1] == 4:
        # If there are four columns, drop the fourth column
        df = df.drop(columns=[3])

    # Assign column names
    df.columns = ['Time Step', 'Concentration', 'Flow-Time']

    # Convert 1.6e-5 concentration to desired amount
    df['Concentration'] *= 5.4

    # Convert concentration to ppm
    df['Concentration'] *= 1e6

    # Convert flow-time to hours
    df['Flow-Time'] /= 3600

    # Find the index where concentration reaches 16 ppm
    exposure_start = df[df['Concentration'] >= 16].iloc[0]['Flow-Time']
    exposure_end = df[df['Concentration'] >= 16].iloc[-1]['Flow-Time']

    # Calculate exposure duration
    exposure_duration = exposure_end - exposure_start

    # Generate the plot
    plt.plot(df['Flow-Time'], df['Concentration'])

    # Add a horizontal line at concentration of 16 ppm
    plt.axhline(y=16, color='red', linestyle='--')

    plt.xlabel('Flow-Time (hours)', fontsize=font_size, fontfamily=font_family)
    plt.ylabel('Concentration (ppm)', fontsize=font_size, fontfamily=font_family)
    plt.title('Flow-Time vs Concentration', fontsize=font_size, fontfamily=font_family)

    # Set font options for tick labels
    plt.xticks(fontsize=font_size-2, fontfamily=font_family)
    plt.yticks(fontsize=font_size-2, fontfamily=font_family)

    # Set x-axis limits
    plt.xlim(0, 12)
    plt.ylim(0, 50)

    # Set color palette
    sns.set_palette(color_palette)

    # Set seaborn style
    sns.set_style('ticks')

    # Find the position for the text within the plot
    text_x = exposure_start + (exposure_duration / 2)
    text_y = 16 + (max(df['Concentration']) - 16) / 3

    # # Check if the text position is too close to the curve
    # if text_y < max(df['Concentration']):
    #     text_y += (max(df['Concentration']) - text_y) / 5

    # Add the exposure duration as text on the plot
    text_to_show = f"16 ppm Exposure for {exposure_duration:.2f} hours"
    plt.text(3.8, 20, text_to_show, fontsize=10,fontfamily=font_family,
             bbox=dict(boxstyle='round,pad=0.5', facecolor='white', alpha=0.9))

    # Save the plot as a PNG image
    file_name = os.path.splitext(os.path.basename(file))[0] + '.png'
    save_path = os.path.join(folder_path, file_name)
    plt.savefig(save_path, dpi=dpi)

    # Close the plot
    plt.close()


"""AsyncSession Database Schema
===============================

New in version 4:

- New metadata table for text parameters;

- Add index for timestamp and name in Log table;

- Add index for timestamp and name in Dataset table;

- Add explicit rowid column as primary key for Log and Dataset tables (this was already automatically added
  by SQLite). Remove the synthetic compound primary key.

"""

from sqlalchemy.orm import declarative_base
from sqlalchemy import (
    Column,
    Text,
    Integer,
    Double,
    BLOB,
    UniqueConstraint,
    ForeignKey,
    inspect,
)

database_version = 4.0
Base = declarative_base()

class LogName(Base):
    __tablename__ = "log_names"

    name = Column(Text, primary_key=True)

    def as_dict(self):
        return {
            "name": self.name,
        }

class Log(Base):
    __tablename__ = "log"

    __table_args__ = (UniqueConstraint("name", "timestamp"),)

    rowid = Column(Integer, primary_key=True)
    timestamp = Column(Integer, index=True)
    name = Column(Text, ForeignKey(LogName.name), index=True)
    value = Column(Double)

    def as_dict(self):
        return {
            "rowid": self.rowid,
            "timestamp": self.timestamp,
            "name": self.name,
            "value": self.value,
        }

class DatasetName(Base):
    __tablename__ = "dataset_names"

    name = Column(Text, primary_key=True)

    def as_dict(self):
        return {
            "name": self.name,
        }

class Dataset(Base):
    __tablename__ = "dataset"

    __table_args__ = (UniqueConstraint("name", "timestamp"),)

    rowid = Column(Integer, primary_key=True)
    timestamp = Column(Integer, index=True)
    name = Column(Text, ForeignKey(DatasetName.name), index=True)
    data = Column(BLOB)

    def as_dict(self):
        return {
            "rowid": self.rowid,
            "timestamp": self.timestamp,
            "name": self.name,
            "data": self.data,
        }

class Parameter(Base):
    __tablename__ = "parameters"

    name = Column(Text, primary_key=True)
    value = Column(Double)

    def as_dict(self):
        return {
            "name": self.name,
            "value": self.value,
        }

class Metadata(Base):
    __tablename__ = "metadata"

    name = Column(Text, primary_key=True)
    value = Column(Text)

    def as_dict(self):
        return {
            "name": self.name,
            "value": self.value,
        }

def create_tables(engine):
    new = False
    if not inspect(engine).has_table("log_names"):
        LogName.metadata.create_all(engine)
        new = True
    if not inspect(engine).has_table("log"):
        Log.metadata.create_all(engine)
        new = True
    if not inspect(engine).has_table("dataset_names"):
        DatasetName.metadata.create_all(engine)
        new = True
    if not inspect(engine).has_table("dataset"):
        Dataset.metadata.create_all(engine)
        new = True
    if not inspect(engine).has_table("parameters"):
        Parameter.metadata.create_all(engine)
        new = True
    if not inspect(engine).has_table("metadata"):
        Metadata.metadata.create_all(engine)
        new = True
    return new

def copy_table(input_session, output_session, table):
    for r in input_session.query(table).yield_per(10000):
        output_session.add(table(**r.as_dict()))
    output_session.commit()

table_list = [
    LogName,
    Log,
    DatasetName,
    Dataset,
    Parameter,
    Metadata,
]


import joblib
from flask import Flask, request, jsonify
from flask_cors import CORS
import os
from MovieReviewClassifierHelpers import preprocessor, tokenizer

app = Flask(__name__)
CORS(app)

@app.route('/predict', methods=['POST'])
def predict():
    if request.method == 'POST':
        # get our review from the request
        review = request.form.get('review')
        if review is None:
            return jsonify({"error": "Please enter a review"}), 422

        # load our classifier with joblib
        lr_classifier = joblib.load(os.path.join(os.path.dirname(os.path.realpath(__file__)), 'lr_pipeline.pkl'))

        # set up our outcome labels
        label = {0: 'negative', 1: 'positive'}

        # get our results
        result = {
            "label": label[lr_classifier.predict([review])[0]],
            "probability": str(round(lr_classifier.predict_proba([review]).max() * 100, 2))
        }
        # return as json
        return jsonify(result), 200


class Node:

    def __init__(self,data):

        self.left=None
        self.right=None
        self.data=data

class Tree:

    def __init__(self):

        self.root=None

    def get_root (self, value):

        return self.root

    def add(self,value):
        """[This methos check whether the node is root or not and if not then call recurcive _add(value,node) to
        add the node on either left or right.]

        Args:
            value ([number]): [value of node]
        """

        if self.root is None:

            self.root=Node(value)

        else:

            self._add(value,self.root)

    def _add(self,value, node):
        """[summary]

        Args:
            value ([value]): [value of node]
            node ([node]): [every node has two left and right and data value]
        """

        if value < node.data:

            if node.left is not None:

                self._add (value,node.left)
            else:
                node.left=Node(value)

        else:
            if node.right is not None:

                self._add(value,node.right)
            else:
                node.right=Node(value)
    def printTree(self):
        """[check if there is no None root and if there is no None
        then call the recursive function _printTree()]"""

        if self.root is not None:

            self._printTree(self.root)

    def _printTree(self, node):
        """[Travers the node.left and print its data and then traverse to node.right]

        Args:
            node ([node]): [node.left,node.right,node.data]
        """

        if node is not None:

            self._printTree(node.left)

            print(str(node.data) + ' ')

            self._printTree(node.right)

tree=Tree()

tree.add(50)
tree.add(1)
tree.add(2)
tree.add(3)
tree.add(4)
tree.printTree()


"""
urls for carpathians
"""

from django.urls import path
from gpsfun.carpathians.views import (
    ridges, ridge, peak, route, routes, peaks)

urlpatterns = [
    path('ridges/', ridges, name="ridges"),
    path('ridge/<slug>/', ridge, name="ridge"),
    path('peak/<slug>/', peak, name="peak"),
    path('peaks/', peaks, name="peaks"),
    path('route/<int:route_id>/', route, name="route"),
    path('', routes, name="routes"),
]