import numpy as np
from scipy.io import loadmat

from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import Dense
from tensorflow.keras.optimizers import Adam

class NNController():
    def __init__(self, params, x_dim, u_dim):
        self.x_dim, self.u_dim = x_dim, u_dim
        self.params = params

    def load_samples(self, data_dir):
        """ Load expert trajectories """
        matfile  = data_dir + 'expert.mat'
        cells_file = loadmat(matfile)
        x_trajs = cells_file['x_trajs'][0]
        u_trajs = cells_file['u_trajs'][0]
        num_traj = x_trajs.shape[0]
        print('\nLoading expert trajectories...')
        print('Number of expert trajectories: ', num_traj)
        #print('x_trajs shape: ', x_trajs.shape)
        #print('u_trajs shape: ', u_trajs.shape)
        #print('Example trajectory and inputs:')
        #print(x_trajs[0])
        #print(u_trajs[0])
        # Remove the last state in each trajectory
        for i in range(num_traj):
            x_trajs[i] = x_trajs[i][:-1]
        X = x_trajs[0]
        y = u_trajs[0]
        for i in range(1, num_traj):
            X = np.concatenate((X, x_trajs[i]))
            y = np.concatenate((y, u_trajs[i]))
        return X, y

    def build_model(self):
        # 2 layers
        layer1_size = 10 #10
        layer2_size = 10 #10
        #layer3_size = 20
        #layer4_size = 128
        model = Sequential()
        model.add(Dense(layer1_size, input_shape=(self.x_dim+1,), activation='relu'))
        model.add(Dense(layer2_size, activation='relu'))
        #model.add(Dense(layer3_size, activation='relu'))
        #model.add(Dense(layer4_size, activation='relu'))
        model.add(Dense(self.u_dim))
        return model

    def train(self, model, X, y, data_type, if_compile):
        """ Train NN with either "expert" data or data generated by the current "symbolic" controller """
        if data_type == 'expert':
            print('\nImitation learning with expert data...')
            print('epochs: %d\n' % self.params['expert_epochs'])
            epochs     = self.params['expert_epochs']
            batch_size = self.params['expert_batch_size']
        elif data_type == 'symbolic':
            print('\nImitation learning with symbolic data...')
            epochs     = self.params['symbolic_epochs']
            batch_size = self.params['symbolic_batch_size']
        else:
            assert False, 'Unrecognized data_type'
        #print('X shape:', X.shape)
        #print('y shape:', y.shape)
        if if_compile:
            # Repeated compile does not hurt, except loses optimizer states.
            model.compile(loss='mse', optimizer='adam', metrics=['mae'])
        model.fit(X, y, epochs=epochs, batch_size=batch_size, verbose=0)
        return model


""" The setup script. """

from setuptools import setup, find_packages

requirements = ['requests',
                'PyQt5',
                'PyQt5-stubs',
                'PyYAML',
                'iso8601']

setup_requirements = []

test_requirements = []

setup(name='MBTA',
      version='1.0',
      description='Application to query MBTA endpoint and get the predicted passages.',
      entry_points={
          'console_scripts': [
              'mbta_py=src/mbta_main_app:main',
          ],
      },
      author='Roman Popenov',
      author_email='popenov.r@gmail.com',
      packages=find_packages(include=['MBTA']),
      setup_requires=setup_requirements,
      test_suite='tests',
      tests_require=test_requirements,
      include_package_data=True,
      keywords='MBTA',
      install_requires=requirements,
      url='https://github.com/roman-popenov/MBTA',
      zip_safe=False
      )


"""
Computes the scale factor on the probability distribution needed to yield a desired expected value of 95.025%
We use a root finder to solve the equation 100 * scale * EV = 95.025 for "scale", where EV is the
expected return as a fraction of the total wagered
"""

import numpy as np
from cleoslots.slots_models import CleopatraModel
from scipy.optimize import root_scalar

desired_rtp = 0.95025

nlines = 20

# We only include entries from index 1 and on because index 0 corresponds to losing (i.e. winning 0)
def expected(scale, nlines=1):
    cleo = CleopatraModel(nlines=nlines)
    avg = np.array([np.mean(x) for x in cleo.intervals[1:]])
    probs = scale * cleo.probabilities[1:]
    return np.sum(avg * probs) - desired_rtp

# For the line 1 model
cleopatra = CleopatraModel(nlines=nlines)

average = np.array([np.mean(x) for x in cleopatra.intervals[1:]])
probabilities = cleopatra.probabilities[1:]
print(probabilities)

# find the proper scale
proper_scale = root_scalar(expected, args=(nlines,), x0=.5, x1=.9).root
print('The proper scale is {}'.format(proper_scale))
probabilities *= proper_scale

expected_value = np.sum(average * probabilities)
print('The expected value of your return is {}'.format(expected_value))


# import the necessary packages
from colordescriptor import ColorDescriptor
#from searcher import Searcher
from pymongo import MongoClient
import gridfs as gfs
from searcher_mdb import Searcher
import numpy as np
import argparse
import cv2

client = MongoClient('localhost', 27017)
db = client['FlickrDB']
coll = db['myImages']

fs = gfs.GridFS(db)

# initialize the image descriptor
cd = ColorDescriptor((8, 12, 3))

# load the query image and describe it
query = cv2.imread(args["query"])
#query = cv2.resize(query,(416,416))
#cv2.imwrite('static', query)
features = cd.describe(query)

# perform the search
searcher = Searcher(args["index"])
results = searcher.search(features)

# display the query
cv2.imshow("Query", query)

# loop over the results
for (score, resultID) in results:
	# load the result image and display it
	print(resultID)
	img = coll.find_one({'filename': resultID})['images'][0]
	fout = fs.get(img['imageID'])

	im = np.frombuffer(fout.read(), dtype=np.uint8)
	im = np.reshape(im, img['shape'])
	#print(im)
	cv2.imshow("Result", im)
	cv2.waitKey(0)


import re
import numpy as np
import matplotlib.pyplot as plt

with open("cyl_extras.dat", "r") as f_obj:
    lines = f_obj.readlines()

data = []
for line in lines:
    aux = line.rstrip("\n")
    aux = re.split(r"\s+", aux)
    data.append([float(i) for i in aux])

data = np.array(data)
data = data[data[:, 0] == 1]

ang = data[:, 1]
vol = data[:, 3]
new_dat = np.array([ang, vol*1e9]).T
new_dat = new_dat[::100]
np.savetxt("out.dat", new_dat, fmt="%.2f")
plt.scatter(new_dat[:, 0], new_dat[:,1])
plt.show()


#!/usr/bin/python3.4
import prometheus_client
from flask import Flask, jsonify, request, Response
from redis import StrictRedis
from rq import Queue
from sums import add
from vmcreate import build_system
from checksvc import check_svc
from flask_prometheus import monitor
from get_mets import mets
from prometheus_client import Summary

from random import randrange

REDIS_HOST="172.17.0.1"
REDIS_PORT="6379"

CONTENT_TYPE_LATEST = str('text/plain; version=0.0.4; charset=utf-8')
#REQUEST_TIME = Summary('summary_request_processing_seconds', 'Time spent processing request')

app = Flask(__name__)

q = Queue('default',connection=StrictRedis(host=REDIS_HOST, port=REDIS_PORT))

@app.route('/')
def get_randrange():
    one = request.args.get('one')
    two = request.args.get('two')
    #job = q.enqueue(randrange, start, stop, step, result_ttl=5000)
    job = q.enqueue(add, one, two)
    job_id = job.get_id()
    return job_id

@app.route("/checkservice")
def check_service():
    vm = request.args.get("vmname")
    sv    = request.args.get("svc")
    print("%s %s" %(vm,sv))
    job = q.enqueue(check_svc, vm, sv)
    job_id = job.get_id()
    return job_id

@app.route("/buildvm")
def create_vm():
    vmname = request.args.get('vmname')
    job = q.enqueue(build_system, vmname,timeout=600)
    job_id = job.get_id()
    return job_id

@app.route("/results")
@app.route("/results/<string:job_id>")
def get_results(job_id=None):

    if job_id is None:
       return jsonify(queued_job_ids=q.job_ids)

    job = q.fetch_job(job_id)

    if job.is_failed:
        return 'Job has failed!', 400

    if job.is_finished:
        return jsonify(result=job.result)

    return 'Job has not finished!', 202

@app.route("/metrics")
def get_metrics():
    mets('default')
    #return prometheus_client.generate_latest()
    return Response(prometheus_client.generate_latest(), mimetype='text/plain')

if __name__ == '__main__':
    # Start server
    monitor(app, port=8000)
    app.run(host='0.0.0.0', port=5000)


import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from datetime import datetime
from datetime import time
import os
import matplotlib
from celluloid import Camera

def to_polar(X,Y,Z):
    a = 6378137.000
    b = 6356752.3141
    c = a**2 / b
    e1 = np.sqrt((a**2 - b**2) / a**2)
    e2 = np.sqrt((a**2 - b**2) / b**2)
    p = np.sqrt(X**2 + Y**2)
    lam = np.arctan2(Y , X)
    beta = np.arctan2(a * Z , (b*p))
    fi = np.arctan2((Z+e2**2 * b *np.sin(beta)**3),(p-e1**2 * a * np.cos(beta)**3))
    N = c / np.sqrt(1 + e2**2 * np.cos(fi)**2)
    h = p / np.cos(fi) - N
    return fi,lam,h

def to_SagYukarı(fi,lam):
    a = 6378137.000
    b = 6356752.3141
    c = a**2 / b
    e1 = np.sqrt((a**2 - b**2) / a**2)
    e2 = np.sqrt((a**2 - b**2) / b**2)
    N = c / np.sqrt(1 + e2**2 * np.cos(fi)**2)
    der = 27 *np.pi/ 180
    A0 = 1- e1**2 /4 - 3*e1**4 /64 -5*e1**6 /256 -175*e1**8 /16384
    A2 = 3/8 *(e1**2 +e1**4 /4+ 15*e1**6 /128- 455*e1**8 /4096)
    A4 = 15/256*(e1**4 +3*e1**6 /4 - 77*e1**8/128)
    A6 = 35/3072*(e1**6 -41*e1**8 /32)
    A8 = (-315)/131072 *e1**8
    Sfi = a*(A0*fi-A2*np.sin(2*fi) + A4* np.sin(4*fi) - A6 *np.sin(6*fi) + A8 *np.sin(8*fi))
    lam = lam - der
    u = lam*np.cos(fi)
    t = np.tan(fi)
    n_squared = (e2**2)*np.cos(fi)**2
    x = N*(u+u**3 /6*(1-t**2 +n_squared)+ u**5 /120 *(5- 18*t**2 + t**4 +14*n_squared - 58*t**2
           * n_squared+ 13*n_squared**2 +4 *n_squared**3 -64 *n_squared**2 *t**2 -24*n_squared**3
           *t**2)+ u**7 /5040 *(61- 479*t**2 +179*t**4 -t**6))
    y = N*(Sfi/N+lam**2 *np.sin(fi)* np.cos(fi)/ 2+ lam**4 *np.sin(fi)* np.cos(fi)**3 / 24 *
           (5-t**2 +9*n_squared +4*n_squared**2)+lam**6 *np.sin(fi)* np.cos(fi)**5 /720 *(61-
           58 *t**2 +t**4 +270*n_squared +445*n_squared**2 +324*n_squared**3- 680*n_squared**2 *
           t**2 +88*n_squared**4 -600*n_squared**4 -600*n_squared**3 *t**2 -192*n_squared**4 *t**2)+
           lam**8 *np.sin(fi)* np.cos(fi)**7 /40320 *(1385- 311*t**2 +543*t**4 -t**6))

    conv = lam*np.sin(fi)*(1+ lam**2 *np.cos(fi)**2 /3 *(1+3*n_squared+2*n_squared**2)+lam**4 * np.cos(fi)**4 /15* (2-t**2))

    yukari = y*0.9996
    saga = x*0.9996 + 500000
    return saga,yukari
def grafnav_read(fname):
    f = open(fname,"r")
    data = f.readlines()
    f.close()
    for i in range(len(data)):
        if len(data[i].split()) != 0  and data[i].split()[0] == "(HMS)":
            break
        else:
            continue

    del data[:i+1]
    easting = []
    northing = []
    height = []
    epoch = []

    for i in range(len(data)):
        if len(data[i].split()) == 0:
            break
        else:
            epoch.append(datetime.strptime(("2021-10-26"+" "+str(data[i].split()[0])),"%Y-%m-%d %H:%M:%S.%f"))
            height.append(float(data[i].split()[3]))
            easting.append(float(data[i].split()[1]))
            northing.append(float(data[i].split()[2]))
    return epoch, easting, northing, height

def output_read(fname):
    # PPP-WIZARD output reader (verbose on)
    f = open(fname,"r")
    data = f.readlines()
    f.close()

    easting = []
    northing = []
    height = []
    X_list = []
    Y_list = []
    Z_list = []
    XS_list = []
    YS_list = []
    ZS_list = []
    epoch = []

    for i in range(len(data)):
        epoch.append(datetime.strptime(("20"+str(data[i].split()[0])+" "+str(data[i].split()[1])),"%Y-%m-%d %H:%M:%S.%f"))
        X = float(data[i].split()[12])
        Y = float(data[i].split()[15])
        Z = float(data[i].split()[18])
        X_list.append(X)
        Y_list.append(Y)
        Z_list.append(Z)
        XS_list.append(float(data[i].split()[14]))
        YS_list.append(float(data[i].split()[17]))
        ZS_list.append(float(data[i].split()[20]))
        fi,lam,h = to_polar(X,Y,Z)
        S,Y = to_SagYukarı(fi,lam)
        height.append(h)
        easting.append(S)
        northing.append(Y)
    return epoch, easting, northing, height, X_list, Y_list, Z_list,XS_list,YS_list,ZS_list

def Sdisp2vel(data,dt = 1):
    vel = []
    i = 1
    while i < (len(data)):
        vel.append((data[i-1]**2 + data[i]**2)**0.5 /dt)
        i+=1
    return vel

def disp2vel(data,dt = 1):
    vel = []
    i = 1
    while i < (len(data)):
        vel.append((data[i] - data[i-1])/dt)
        i+=1
    return vel

def vel2acc(data,dt = 1):
    acc = []
    i = 1
    while i < (len(data)):
        acc.append((data[i]-data[i-1])/dt)
        i+=1
    return acc
def graph_E_N2(var1,var2,Svar1,Svar2,Tvar1,Tvar2,fname):
    var1 = np.array(var1)
    var2 = np.array(var2)
    Svar1 = np.array(Svar1)
    Svar2 = np.array(Svar2)
    Tvar1 = np.array(Tvar1)
    Tvar2 = np.array(Tvar2)

    #matplotlib.use('Agg')
    fig = plt.figure(figsize=(10,7))
    plt.rcParams.update({'font.size': 9})

    plt.grid(True, linestyle='--',linewidth=0.2)
    plt.title("2D Scatter Plot",  fontsize=10)
    plt.ylabel("Northing (\u0394X) (cm)",  fontsize=9)
    plt.xlabel("Easting (\u0394Y) (cm)",  fontsize=9)

    xax = plt.gca().axes.get_xaxis()
    xax = xax.set_visible(False)

    # set visibility of y-axis as False
    yax = plt.gca().axes.get_yaxis()
    yax = yax.set_visible(False)
    for i in range(len(var1)):
        plt.scatter(var2[:i],var1[:i],c='b',marker="o",s = 2,label="rover")
        plt.scatter(Svar2[:i],Svar1[:i],c='r',marker="o", s = 4,label="filtered rover")
        plt.scatter(Tvar2[:i],Tvar1[:i],c='g',marker="o", s = 2,label="Predicted rover")

        plt.pause(0.001)
    plt.show()

def graph_E_N(var1,var2,Svar1,Svar2,Tvar1,Tvar2,Fvar1,Fvar2,fname):
    var1 = np.array(var1)
    var2 = np.array(var2)
    Svar1 = np.array(Svar1)
    Svar2 = np.array(Svar2)
    Tvar1 = np.array(Tvar1)
    Tvar2 = np.array(Tvar2)
    Fvar1 = np.array(Fvar1)
    Fvar2 = np.array(Fvar2)

    fig = plt.figure(figsize=(20,15))
    plt.plot(var2,var1,color='b',marker="o", linestyle="None",markersize = 2,label="rover")
    plt.plot(Svar2,Svar1,color='r',marker="o", linestyle="None", markersize = 4,label="filtered rover")
    plt.plot(Tvar2,Tvar1,color='g',marker="o", linestyle="None",markersize = 2,label="Predicted rover")
    plt.plot(Fvar2,Fvar1,color='black',marker="o", linestyle="None",markersize = 2,label="Fixed rover")
    plt.grid(True, linestyle='--',linewidth=0.2)
    plt.legend(loc=1, handlelength=1,  fontsize=12,markerscale=1)
    plt.title("2D Scatter Plot",  fontsize=12)
    plt.ylabel("Northing (\u0394X) (cm)",  fontsize=12)
    plt.xlabel("Easting (\u0394Y) (cm)",  fontsize=12)
    xax = plt.gca().axes.get_xaxis()
    xax = xax.set_visible(False)
    yax = plt.gca().axes.get_yaxis()
    yax = yax.set_visible(False)
    plt.tight_layout()
    plt.savefig("kalman2d.png")

def kalman(Xmatrx, obs_err, init, pro_err,aX):
    i           = int(np.size(Xmatrx)/len(Xmatrx))

    A           = np.identity(i)
    pc = 0
    while pc < i:
        A[pc][pc+1] = init[1]
        pc += 2

    B           = np.identity(i)
    pc = 0
    while pc < i:
        B[pc][pc] = 0.5 * init[1]**2
        B[pc+1][pc+1] = init[1]
        pc += 2

    H           =  np.identity(i)
    for pc in range(i):
        if pc % 2 != 0:
            H[pc][pc] = 0
    C           = np.identity(i)
    I           = np.identity(i)

    X_kp        = np.ndarray((i,1), dtype = float)
    X_k_1       = np.array(Xmatrx[0]).reshape((-1,1))
    P_k_1       = np.ndarray((i,i), dtype = float)
    for k in range(i):
        P_k_1[k][k] = pro_err[k]**2

    R           = np.ndarray((i,i), dtype = float)

    x_k         = np.ndarray((len(Xmatrx)-1,3), dtype = float)
    x_p         = np.ndarray((len(Xmatrx)-1,3), dtype = float)
    x_m         = np.ndarray((len(Xmatrx)-1,3), dtype = float)

    k, j        = 0, 1
    while j < len(Xmatrx):
        for pc in range(i):
            R[pc][pc]           = obs_err[k][pc]**2
        X_kp            = np.dot(A, X_k_1) + np.dot(B, np.array([aX[k]]).T)
        P_kp            = (np.dot(np.dot(A, P_k_1), np.transpose(A)))
        for pc in range(i):
            for pk in range(i):
                if pc !=pk:
                    P_kp[pc][pk] = 0

        KG          = np.divide(np.dot(P_kp, H), (np.dot(np.dot(H, P_kp), np.transpose(H)) + R))
        for pc in range(i):
            for pk in range(i):
                if pc !=pk:
                    KG[pc][pk] = 0

        Y_k         = np.dot(H, Xmatrx[j].reshape((-1, 1)))
        X_k         = X_kp + np.dot(KG, (Y_k - np.dot(H, X_kp)))
        P_k         = np.dot((I - np.dot(KG, H)), P_kp)

        X_k_1       = X_k
        P_k_1       = P_k
        x_k[k]      = X_k[0][0],X_k[2][0],X_k[4][0]
        x_p[k]      = X_kp[0][0],X_kp[2][0],X_kp[4][0]
        x_m[k]      = Xmatrx[j][0],Xmatrx[j][2],Xmatrx[j][4]

        j += 1
        k += 1
    return x_m, x_k, x_p
epoch, easting, northing, height, X_list, Y_list, Z_list,XS_list,YS_list,ZS_list = output_read("output_lowlevel_itu_kampus_RAIM")

np.seterr(divide = 'ignore', invalid = 'ignore')

vX = disp2vel(X_list)
aX = disp2vel(vX)
SvX = Sdisp2vel(XS_list)
SaX = Sdisp2vel(SvX)
SaX.append(0)
aX.append(0)
del X_list[-1]
del XS_list[-1]

vY = disp2vel(Y_list)
aY = disp2vel(vY)
SvY = Sdisp2vel(YS_list)
SaY = Sdisp2vel(SvY)
SaY.append(0)
aY.append(0)
del Y_list[-1]
del YS_list[-1]

vZ = disp2vel(Z_list)
aZ = disp2vel(vZ)
SvZ = Sdisp2vel(ZS_list)
SaZ = Sdisp2vel(SvZ)
SaZ.append(0)
aZ.append(0)
del Z_list[-1]
del ZS_list[-1]

X               = np.array((list(zip(X_list, vX, Y_list, vY ,Z_list, vZ))))
obs_err         = np.array((list(zip(XS_list, SvX,YS_list, SvY, ZS_list, SvZ))), dtype = float)
pro_err         = np.array(([np.mean(XS_list), np.mean(SvX), np.mean(YS_list), np.mean(SvY), np.mean(ZS_list), np.mean(SvZ)]), dtype = float)
init            = np.array(([10],[1],[0]), dtype = float)
A               = np.array((list(zip(aX, aX, aY, aY ,aZ, aZ))))
x_m, x_k, x_p   = kalman(X, obs_err, init, pro_err,A)

height = []
northing = []
easting = []
height2 = []
northing2 = []
easting2 = []
height3 = []
northing3 = []
easting3 = []

for i in range(len(x_m)):

    fi,lam,h = to_polar(x_m[i][0],x_m[i][1],x_m[i][2])
    S,Y = to_SagYukarı(fi,lam)
    height.append(h)
    easting.append(S)
    northing.append(Y)

for i in range(len(x_k)):

    fi,lam,h = to_polar(x_k[i][0],x_k[i][1],x_k[i][2])
    S,Y = to_SagYukarı(fi,lam)
    height2.append(h)
    easting2.append(S)
    northing2.append(Y)

for i in range(len(x_p)):
    fi,lam,h = to_polar(x_p[i][0],x_p[i][1],x_p[i][2])
    S,Y = to_SagYukarı(fi,lam)
    height3.append(h)
    easting3.append(S)
    northing3.append(Y)

epoch4, easting4, northing4, height4 = grafnav_read("grafnav-brodcastorbitilePPK-chc-rt-ppp-rinex304v2.txt")

graph_E_N(northing,easting,northing2,easting2,northing3,easting3,northing4,easting4,"kalman")
fig = plt.figure()
ax = plt.axes(projection ='3d')

ax.plot3D(northing3,easting3, height3, 'green')
plt.show()
ax.plot3D(northing4,easting4, height4, 'green')
plt.show()


"""Private message list views."""

# Python
# import pdb

# Django
from django.db.models import Exists, OuterRef, Q
from django.views.generic import ListView

# Pyrty
from privatemessages.forms import PrivateMessageForm
from privatemessages.models import PrivateMessage
from users.models import User

class PrivateMessageList(ListView):
	"""Display inbox with list of messages from a user."""

	model = PrivateMessage
	paginate_by = 10

	def get_context_data(self, **kwargs):
		context = super(PrivateMessageList, self).get_context_data(**kwargs)
		context['current_inbox'] = self.kwargs['origin_user']
		context['inbox'] = User.objects.filter(
			Exists(PrivateMessage.objects.filter(origin_user=OuterRef('pk'))) |
            Exists(PrivateMessage.objects.filter(target_user=OuterRef('pk')))
		)
		context['private_message_form'] = PrivateMessageForm(
			target_user=User.objects.get(username=self.kwargs['origin_user'])
		)
		return context

	def get_queryset(self, queryset=None):
		private_messages = PrivateMessage.objects\
			.select_related('origin_user').filter(
			Q(target_user=self.request.user, origin_user__username=self.kwargs['origin_user']) |
			Q(target_user__username=self.kwargs['origin_user'], origin_user=self.request.user)
		).order_by('created')
		PrivateMessage.objects.filter(target_user=self.request.user).update(seen=True)
		return private_messages


import math
from collections import OrderedDict

from .. import numpy
from ..util import use_np

from ..base import numeric_types, string_types
from .. import ndarray, npx
from .. import registry

def check_label_shapes(labels, preds, wrap=False, shape=False):
    """Helper function for checking shape of label and prediction

    Parameters
    ----------
    labels : list of `NDArray`
        The labels of the data.

    preds : list of `NDArray`
        Predicted values.

    wrap : boolean
        If True, wrap labels/preds in a list if they are single NDArray

    shape : boolean
        If True, check the shape of labels and preds;
        Otherwise only check their length.
    """
    if not shape:
        label_shape, pred_shape = len(labels), len(preds)
    else:
        label_shape, pred_shape = labels.shape, preds.shape

    if label_shape != pred_shape:
        raise ValueError("Shape of labels {} does not match shape of "
                         "predictions {}".format(label_shape, pred_shape))

    if wrap:
        if isinstance(labels, ndarray.ndarray.NDArray):
            labels = [labels]
        if isinstance(preds, ndarray.ndarray.NDArray):
            preds = [preds]

    return labels, preds

class EvalMetric(object):
    """Base class for all evaluation metrics.

    .. note::

        This is a base class that provides common metric interfaces.
        One should not use this class directly, but instead create new metric
        classes that extend it.

    Parameters
    ----------
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.
    """
    def __init__(self, name, output_names=None,
                 label_names=None, **kwargs):
        self.name = str(name)
        self.output_names = output_names
        self.label_names = label_names
        self._kwargs = kwargs
        self.reset()

    def __str__(self):
        return "EvalMetric: {}".format(dict(self.get_name_value()))

    def get_config(self):
        """Save configurations of metric. Can be recreated
        from configs with metric.create(``**config``)
        """
        config = self._kwargs.copy()
        config.update({
            'metric': self.__class__.__name__,
            'name': self.name,
            'output_names': self.output_names,
            'label_names': self.label_names})
        return config

    def update_dict(self, label, pred):
        """Update the internal evaluation with named label and pred

        Parameters
        ----------
        labels : OrderedDict of str -> NDArray
            name to array mapping for labels.

        preds : OrderedDict of str -> NDArray
            name to array mapping of predicted outputs.
        """
        if self.output_names is not None:
            pred = [pred[name] for name in self.output_names]
        else:
            pred = list(pred.values())

        if self.label_names is not None:
            label = [label[name] for name in self.label_names]
        else:
            label = list(label.values())

        self.update(label, pred)

    def update(self, labels, preds):
        """Updates the internal evaluation result.

        Parameters
        ----------
        labels : list of `NDArray`
            The labels of the data.

        preds : list of `NDArray`
            Predicted values.
        """
        raise NotImplementedError()

    def reset(self):
        """Resets the internal evaluation result to initial state."""
        self.num_inst = 0
        self.sum_metric = 0.0

    def get(self):
        """Gets the current evaluation result.

        Returns
        -------
        names : list of str
           Name of the metrics.
        values : list of float
           Value of the evaluations.
        """
        if self.num_inst == 0:
            return (self.name, float('nan'))
        else:
            res = self.sum_metric / self.num_inst
            if isinstance(res, numpy.ndarray) and len(res.shape) == 0:
                # currently calling ' c = mxnet.numpy.array([1,2,3]).sum() ' would get
                # ' array(6.) ', a ndarray with shape ()
                # In this case, returning a 'float' in .get() is more explicit.
                res = res.item()
            return (self.name, res)

    def get_name_value(self):
        """Returns zipped name and value pairs.

        Returns
        -------
        list of tuples
            A (name, value) tuple list.
        """
        name, value = self.get()
        if not isinstance(name, list):
            name = [name]
        if not isinstance(value, list):
            value = [value]
        return list(zip(name, value))

# pylint: disable=invalid-name
register = registry.get_register_func(EvalMetric, 'metric')
alias = registry.get_alias_func(EvalMetric, 'metric')
_create = registry.get_create_func(EvalMetric, 'metric')
# pylint: enable=invalid-name

def create(metric, *args, **kwargs):
    """Creates evaluation metric from metric names or instances of EvalMetric
    or a custom metric function.

    Parameters
    ----------
    metric : str or callable
        Specifies the metric to create.
        This argument must be one of the below:

        - Name of a metric.
        - An instance of `EvalMetric`.
        - A list, each element of which is a metric or a metric name.
        - An evaluation function that computes custom metric for a given batch of
          labels and predictions.
    *args : list
        Additional arguments to metric constructor.
        Only used when metric is str.
    **kwargs : dict
        Additional arguments to metric constructor.
        Only used when metric is str

    Examples
    --------
    >>> def custom_metric(label, pred):
    ...     return np.mean(np.abs(label - pred))
    ...
    >>> metric1 = mx.gluon.metric.create('acc')
    >>> metric2 = mx.gluon.metric.create(custom_metric)
    >>> metric3 = mx.gluon.metric.create([metric1, metric2, 'rmse'])
    """
    if callable(metric):
        return CustomMetric(metric, *args, **kwargs)
    elif isinstance(metric, list):
        composite_metric = CompositeEvalMetric()
        for child_metric in metric:
            composite_metric.add(create(child_metric, *args, **kwargs))
        return composite_metric

    return _create(metric, *args, **kwargs)

@register
@alias('composite')
class CompositeEvalMetric(EvalMetric):
    """Manages multiple evaluation metrics.

    Parameters
    ----------
    metrics : list of EvalMetric
        List of child metrics.
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.

    Examples
    --------
    >>> predicts = [mx.np.array([[0.3, 0.7], [0, 1.], [0.4, 0.6]])]
    >>> labels   = [mx.np.array([0, 1, 1])]
    >>> eval_metrics_1 = mx.gluon.metric.Accuracy()
    >>> eval_metrics_2 = mx.gluon.metric.F1()
    >>> eval_metrics = mx.gluon.metric.CompositeEvalMetric()
    >>> for child_metric in [eval_metrics_1, eval_metrics_2]:
    >>>     eval_metrics.add(child_metric)
    >>> eval_metrics.update(labels = labels, preds = predicts)
    >>> eval_metrics.get()
    (['accuracy', 'f1'], [0.6666666666666666, 0.8])
    """

    def __init__(self, metrics=None, name='composite',
                 output_names=None, label_names=None):
        super(CompositeEvalMetric, self).__init__(
            name, output_names=output_names, label_names=label_names)
        if metrics is None:
            metrics = []
        self.metrics = [create(i) for i in metrics]

    def add(self, metric):
        """Adds a child metric.

        Parameters
        ----------
        metric
            A metric instance.
        """
        self.metrics.append(create(metric))

    def get_metric(self, index):
        """Returns a child metric.

        Parameters
        ----------
        index : int
            Index of child metric in the list of metrics.
        """
        try:
            return self.metrics[index]
        except IndexError:
            return ValueError("Metric index {} is out of range 0 and {}".format(
                index, len(self.metrics)))

    def update_dict(self, labels, preds): # pylint: disable=arguments-differ
        if self.label_names is not None:
            labels = OrderedDict([i for i in labels.items()
                                  if i[0] in self.label_names])
        if self.output_names is not None:
            preds = OrderedDict([i for i in preds.items()
                                 if i[0] in self.output_names])

        for metric in self.metrics:
            metric.update_dict(labels, preds)

    def update(self, labels, preds):
        """Updates the internal evaluation result.

        Parameters
        ----------
        labels : list of `NDArray`
            The labels of the data.

        preds : list of `NDArray`
            Predicted values.
        """
        for metric in self.metrics:
            metric.update(labels, preds)

    def reset(self):
        """Resets the internal evaluation result to initial state."""
        try:
            for metric in self.metrics:
                metric.reset()
        except AttributeError:
            pass

    def get(self):
        """Returns the current evaluation result.

        Returns
        -------
        names : list of str
           Name of the metrics.
        values : list of float
           Value of the evaluations.
        """
        names = []
        values = []
        for metric in self.metrics:
            name, value = metric.get()
            if isinstance(name, string_types):
                name = [name]
            if isinstance(value, numeric_types):
                value = [value]
            names.extend(name)
            values.extend(value)
        return (names, values)

    def get_config(self):
        config = super(CompositeEvalMetric, self).get_config()
        config.update({'metrics': [i.get_config() for i in self.metrics]})
        return config

########################
# CLASSIFICATION METRICS
########################

@register
@alias('acc')
@use_np
class Accuracy(EvalMetric):
    """Computes accuracy classification score.

    The accuracy score is defined as

    .. math::

        \\text{accuracy}(y, \\hat{y}) = \\frac{1}{n} \\sum_{i=0}^{n-1}
        \\text{1}(\\hat{y_i} == y_i)

    Parameters
    ----------
    axis : int, default=1
        The axis that represents classes
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.

    Examples
    --------
    >>> predicts = [mx.np.array([[0.3, 0.7], [0, 1.], [0.4, 0.6]])]
    >>> labels   = [mx.np.array([0, 1, 1])]
    >>> acc = mx.gluon.metric.Accuracy()
    >>> acc.update(preds = predicts, labels = labels)
    >>> acc.get()
    ('accuracy', 0.6666666666666666)
    """
    def __init__(self, axis=1, name='accuracy',
                 output_names=None, label_names=None):
        super(Accuracy, self).__init__(
            name, axis=axis,
            output_names=output_names, label_names=label_names)
        self.axis = axis

    def update(self, labels, preds):
        """Updates the internal evaluation result.

        Parameters
        ----------
        labels : list of `NDArray`
            The labels of the data with class indices as values, one per sample.

        preds : list of `NDArray`
            Prediction values for samples. Each prediction value can either be the class index,
            or a vector of likelihoods for all classes.
        """
        labels, preds = check_label_shapes(labels, preds, True)

        for label, pred_label in zip(labels, preds):
            pred_label = pred_label.as_np_ndarray().to_device(label.device)
            label = label.as_np_ndarray()
            if pred_label.shape != label.shape:
                pred_label = pred_label.argmax(axis=self.axis)
            pred_label = pred_label.astype('int32')
            label = label.astype('int32')
            # flatten before checking shapes to avoid shape miss match
            label = label.reshape(-1)
            pred_label = pred_label.reshape(-1)

            check_label_shapes(label, pred_label)

            num_correct = (pred_label == label).sum().astype('float64')
            self.sum_metric += num_correct
            self.num_inst += len(pred_label)

@register
@alias('top_k_accuracy', 'top_k_acc')
@use_np
class TopKAccuracy(EvalMetric):
    """Computes top k predictions accuracy.

    `TopKAccuracy` differs from Accuracy in that it considers the prediction
    to be ``True`` as long as the ground truth label is in the top K
    predicated labels.

    If `top_k` = ``1``, then `TopKAccuracy` is identical to `Accuracy`.

    Parameters
    ----------
    top_k : int
        Whether targets are in top k predictions.
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.

    Examples
    --------
    >>> np.random.seed(999)
    >>> top_k = 3
    >>> labels = [mx.np.array([2, 6, 9, 2, 3, 4, 7, 8, 9, 6])]
    >>> predicts = [mx.np.array(np.random.rand(10, 10))]
    >>> acc = mx.gluon.metric.TopKAccuracy(top_k=top_k)
    >>> acc.update(labels, predicts)
    >>> acc.get()
    ('top_k_accuracy', 0.3)
    """

    def __init__(self, top_k=1, name='top_k_accuracy',
                 output_names=None, label_names=None):
        super(TopKAccuracy, self).__init__(
            name, top_k=top_k,
            output_names=output_names, label_names=label_names)
        self.top_k = top_k
        assert(self.top_k > 1), 'Please use Accuracy if top_k is no more than 1'
        self.name += f'_{self.top_k}'

    def update(self, labels, preds):
        """Updates the internal evaluation result.

        Parameters
        ----------
        labels : list of `NDArray`
            The labels of the data.

        preds : list of `NDArray`
            Predicted values.
        """
        labels, preds = check_label_shapes(labels, preds, True)

        for label, pred_label in zip(labels, preds):
            assert(len(pred_label.shape) <= 2), 'Predictions should be no more than 2 dims'
            # Using argpartition here instead of argsort is safe because
            # we do not care about the order of top k elements. It is
            # much faster, which is important since that computation is
            # single-threaded due to Python GIL.
            pred_label = pred_label.as_np_ndarray().to_device(label.device).astype('float32')
            pred_label = numpy.argpartition(pred_label, -self.top_k).to_device(label.device)
            label = label.as_np_ndarray().astype('int32')
            check_label_shapes(label, pred_label)
            num_samples = pred_label.shape[0]
            num_dims = len(pred_label.shape)
            if num_dims == 1:
                num_correct = (pred_label.reshape(-1) == label.reshape(-1)).sum()
                self.sum_metric += num_correct.astype('float64')
            elif num_dims == 2:
                num_classes = pred_label.shape[1]
                top_k = min(num_classes, self.top_k)
                for j in range(top_k):
                    num_correct = (pred_label[:, num_classes - 1 - j].reshape(-1) == label.reshape(-1)).sum()
                    self.sum_metric += num_correct.astype('float64')
            self.num_inst += num_samples

def predict_with_threshold(pred, threshold=0.5):
    """Do thresholding of predictions in binary and multilabel cases.

    Parameters
    ----------
    preds : ndarray
        predictions in shape of (batch_size, ...) or (batch_size, ..., num_categories)

    preds : float or ndarray
        threshold（s) in shape of float or (num_categories)
    """
    if isinstance(threshold, float):
        return pred > threshold
    elif isinstance(threshold, (numpy.ndarray, ndarray.ndarray.NDArray)):
        num_classes = pred.shape[-1]
        assert threshold.shape[-1] == num_classes, \
                f"shape mismatch: {pred.shape[-1]} vs. {threshold.shape[-1]}"
        return pred > threshold
    else:
        raise ValueError("{} is a wrong type for threshold!".format(type(threshold)))

def one_hot(idx, num):
    return (numpy.arange(num).astype(idx) == idx[:, None]).astype('int32')

@use_np
class _ClassificationMetrics(object):
    """Private container class for classification metric statistics.

    True/false positive and true/false negative counts are sufficient statistics for various classification metrics.
    This class provides the machinery to track those statistics across mini-batches of
    (label, prediction) pairs.

    Parameters
    ----------
    class_type : str, default "binary"
        "binary": f1 for binary classification.
        "multiclass": f1 for multiclassification problem.
        "multilabel": f1 for multilabel classification.
    beta : float, default 1
        weight of precision in harmonic mean.
    threshold : float, default 0.5
        threshold for deciding whether the predictions are positive or negative.

    """

    def __init__(self, class_type="binary", threshold=0.5, beta=1):
        self.class_type = class_type
        self.threshold = threshold
        self.beta = beta
        self.reset_stats()

    def _set(self, num, device):
        if self.num_classes is None:
            self.num_classes = num
            self.true_positives = numpy.zeros(num, dtype='float64').to_device(device)
            self.false_negatives = numpy.zeros(num, dtype='float64').to_device(device)
            self.false_positives = numpy.zeros(num, dtype='float64').to_device(device)
            self.true_negatives = numpy.zeros(num, dtype='float64').to_device(device)
        else:
            assert self.num_classes == num, \
                "Input number of classes has changed from {} to {}".format(self.num_classes, num)

    def update_stats(self, label, pred):
        """Update various binary classification counts for a single (label, pred) pair.

        Parameters
        ----------
        label : `NDArray`
            The labels of the data.

        pred : `NDArray`
            Predicted values.
        """
        pred = pred.as_np_ndarray().to_device(label.device)
        label = label.as_np_ndarray().astype('int32')
        if self.class_type == "binary":
            self._set(1, label.device)
            if label.max() > 1:
                raise ValueError("Wrong label for binary classification.")
            if pred.shape == label.shape:
                pass
            elif pred.shape[-1] > 2:
                raise ValueError("The shape of prediction {} is wrong for binary classification.".format(pred.shape))
            elif pred.shape[-1] == 2:
                pred = pred.reshape(-1, 2)[:, 1]
            pred_label = predict_with_threshold(pred, self.threshold).reshape(-1)
            label = label.reshape(-1)

        elif self.class_type == "multiclass":
            num = pred.shape[-1]
            self._set(num, label.device)
            assert label.max() < num, "pred contains fewer classes than label!"
            pred_label = one_hot(pred.argmax(axis=-1).reshape(-1), num)
            label = one_hot(label.reshape(-1), num)

        elif self.class_type == "multilabel":
            num = pred.shape[-1]
            self._set(num, label.device)
            assert pred.shape == label.shape, \
                "The shape of label should be same as that of prediction for multilabel classification."
            pred_label = predict_with_threshold(pred, self.threshold).reshape(-1, num)
            label = label.reshape(-1, num)
        else:
            raise ValueError(
                "Wrong class_type {}! Only supports ['binary', 'multiclass', 'multilabel']".format(self.class_type))

        check_label_shapes(label, pred_label)

        pred_true = (pred_label == 1)
        pred_false = (pred_label == 0)
        label_true = (label == 1)
        label_false = (label == 0)

        true_pos = (pred_true * label_true).sum(0)
        false_pos = (pred_true * label_false).sum(0)
        false_neg = (pred_false * label_true).sum(0)
        true_neg = (pred_false * label_false).sum(0)
        self.true_positives += true_pos
        self.false_positives += false_pos
        self.false_negatives += false_neg
        self.true_negatives += true_neg

    @property
    def precision(self):
        if self.num_classes is not None:
            return self.true_positives / numpy.maximum(self.true_positives + self.false_positives, 1e-12)
        else:
            return 0.

    @property
    def micro_precision(self):
        if self.num_classes is not None:
            return self.true_positives.sum() / \
                numpy.maximum(self.true_positives.sum() + self.false_positives.sum(), 1e-12)
        else:
            return 0.

    @property
    def recall(self):
        if self.num_classes is not None:
            return self.true_positives / numpy.maximum(self.true_positives + self.false_negatives, 1e-12)
        else:
            return 0.

    @property
    def micro_recall(self):
        if self.num_classes is not None:
            return self.true_positives.sum() / \
                numpy.maximum(self.true_positives.sum() + self.false_negatives.sum(), 1e-12)
        else:
            return 0.

    @property
    def fscore(self):
        return (1 + self.beta ** 2) * self.precision * self.recall / \
            numpy.maximum(self.beta ** 2 * self.precision + self.recall, 1e-12)

    @property
    def micro_fscore(self):
        if self.micro_precision + self.micro_recall > 0:
            return (1 + self.beta ** 2) * self.micro_precision * self.micro_recall / \
                (self.beta ** 2 * self.micro_precision + self.micro_recall)
        else:
            return 0.

    def binary_matthewscc(self):
        """Calculate the Matthew's Correlation Coefficent"""
        if not self.total_examples:
            return 0.

        true_pos = float(self.true_positives)
        false_pos = float(self.false_positives)
        false_neg = float(self.false_negatives)
        true_neg = float(self.true_negatives)

        terms = [(true_pos + false_pos),
                 (true_pos + false_neg),
                 (true_neg + false_pos),
                 (true_neg + false_neg)]
        denom = 1.
        for t in filter(lambda t: t != 0., terms):
            denom *= t
        return ((true_pos * true_neg) - (false_pos * false_neg)) / math.sqrt(denom)

    @property
    def total_examples(self):
        if self.num_classes is None:
            return 0
        return int(self.false_negatives[0] + self.false_positives[0] + \
               self.true_negatives[0] + self.true_positives[0])

    def reset_stats(self):
        self.num_classes = None
        self.true_positives = None
        self.false_negatives = None
        self.false_positives = None
        self.true_negatives = None

@register
@use_np
class F1(EvalMetric):
    """Computes the F1 score of a binary classification problem.

    The F1 score is equivalent to harmonic mean of the precision and recall,
    where the best value is 1.0 and the worst value is 0.0. The formula for F1 score is::

        F1 = 2 * (precision * recall) / (precision + recall)

    The formula for precision and recall is::

        precision = true_positives / (true_positives + false_positives)
        recall    = true_positives / (true_positives + false_negatives)

    .. note::

        This F1 score only supports binary classification.

    Parameters
    ----------
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.
    class_type : str, default "binary"
        "binary": f1 for binary classification.
        "multiclass": f1 for multiclassification problem.
        "multilabel": f1 for multilabel classification.
    threshold : float, default 0.5
        threshold for postive confidence value.
    average : str, default 'micro'
        Strategy to be used for aggregating across mini-batches.
            "macro": Calculate metrics for each label and return unweighted mean of f1.
            "micro": Calculate metrics globally by counting the total TP, FN and FP.
            None: Return f1 scores for each class (numpy.ndarray) .

    Examples
    --------
    >>> predicts = [mx.np.array([[0.3, 0.7], [0., 1.], [0.4, 0.6]])]
    >>> labels   = [mx.np.array([0., 1., 1.])]
    >>> f1 = mx.gluon.metric.F1()
    >>> f1.update(preds = predicts, labels = labels)
    >>> f1.get()
    ('f1', 0.8)
    """

    def __init__(self, name='f1',
                 output_names=None, label_names=None, class_type="binary", threshold=0.5, average="micro"):
        self.average = average
        self.metrics = _ClassificationMetrics(class_type=class_type, threshold=threshold)
        EvalMetric.__init__(self, name=name,
                            output_names=output_names, label_names=label_names)

    def update(self, labels, preds):
        """Updates the internal evaluation result.

        Parameters
        ----------
        labels : list of `NDArray`
            The labels of the data.

        preds : list of `NDArray`
            Predicted values.
        """
        labels, preds = check_label_shapes(labels, preds, True)

        for label, pred in zip(labels, preds):
            self.metrics.update_stats(label, pred)

        if self.average == "micro":
            self.sum_metric = self.metrics.micro_fscore * self.metrics.total_examples
        elif self.average == "macro":
            self.sum_metric = self.metrics.fscore.mean() * self.metrics.total_examples
        else:
            self.sum_metric = self.metrics.fscore * self.metrics.total_examples
        self.num_inst = self.metrics.total_examples

    def reset(self):
        """Resets the internal evaluation result to initial state."""
        self.sum_metric = 0.
        self.num_inst = 0
        self.metrics.reset_stats()

@register
@use_np
class Fbeta(F1):
    """Computes the Fbeta score of a binary classification problem.

    The Fbeta score is equivalent to harmonic mean of the precision and recall,
    where the best value is 1.0 and the worst value is 0.0. The formula for Fbeta score is::

        Fbeta = (1 + beta ** 2) * (precision * recall) / (beta ** 2 * precision + recall)

    The formula for precision and recall is::

        precision = true_positives / (true_positives + false_positives)
        recall    = true_positives / (true_positives + false_negatives)

    .. note::

        This Fbeta score only supports binary classification.

    Parameters
    ----------
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.
    class_type : str, default "binary"
        "binary": f1 for binary classification.
        "multiclass": f1 for multiclassification problem.
        "multilabel": f1 for multilabel classification.
    beta : float, default 1
        weight of precision in harmonic mean.
    threshold : float, default 0.5
        threshold for postive confidence value.
    average : str, default 'micro'
        Strategy to be used for aggregating across mini-batches.
            "macro": Calculate metrics for each label and return unweighted mean of f1.
            "micro": Calculate metrics globally by counting the total TP, FN and FP.
            None: Return f1 scores for each class.

    Examples
    --------
    >>> predicts = [mx.np.array([[0.3, 0.7], [0., 1.], [0.4, 0.6]])]
    >>> labels   = [mx.np.array([0., 1., 1.])]
    >>> fbeta = mx.gluon.metric.Fbeta(beta=2)
    >>> fbeta.update(preds = predicts, labels = labels)
    >>> fbeta.get()
    ('fbeta', 0.9090909090909091)
    """

    def __init__(self, name='fbeta',
                 output_names=None, label_names=None, class_type="binary", beta=1, threshold=0.5, average="micro"):
        super(Fbeta, self).__init__(
            name=name, output_names=output_names, label_names=label_names,
            class_type=class_type, threshold=threshold, average=average)
        self.metrics = _ClassificationMetrics(class_type=class_type, threshold=threshold, beta=beta)

@register
@use_np
class BinaryAccuracy(EvalMetric):
    """Computes the accuracy of a binary or multilabel classification problem.

    Parameters
    ----------
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.
    threshold : float or ndarray, default 0.5
        threshold for deciding whether the predictions are positive or negative.

    Examples
    --------
    >>> predicts = [mx.np.array([0.7, 1, 0.55])]
    >>> labels   = [mx.np.array([0., 1., 0.])]
    >>> bacc = mx.gluon.metric.BinaryAccuracy(threshold=0.6)
    >>> bacc.update(preds = predicts, labels = labels)
    >>> bacc.get()
    ('binary_accuracy', 0.6666666666666666)
    """

    def __init__(self, name='binary_accuracy',
                 output_names=None, label_names=None, threshold=0.5):
        self.threshold = threshold
        EvalMetric.__init__(self, name=name,
                            output_names=output_names, label_names=label_names)

    def update(self, labels, preds):
        """Updates the internal evaluation result.

        Parameters
        ----------
        labels : list of `NDArray`
            Each label denotes positive/negative for each class.

        preds : list of `NDArray`
            Each prediction value is a confidence value of being positive for each class.
        """
        labels, preds = check_label_shapes(labels, preds, True)

        for label, pred_label in zip(labels, preds):
            pred_label = predict_with_threshold(pred_label, self.threshold)

            pred_label = pred_label.as_np_ndarray().astype('int32').to_device(label.device)
            label = label.as_np_ndarray().astype('int32')
            # flatten before checking shapes to avoid shape miss match
            label = label.reshape(-1)
            pred_label = pred_label.reshape(-1)

            check_label_shapes(label, pred_label)

            num_correct = (pred_label == label).sum().astype('float64')
            self.sum_metric += num_correct
            self.num_inst += len(pred_label)

@register
@use_np
class MCC(EvalMetric):
    """Computes the Matthews Correlation Coefficient of a binary classification problem.

    While slower to compute than F1 the MCC can give insight that F1 or Accuracy cannot.
    For instance, if the network always predicts the same result
    then the MCC will immeadiately show this. The MCC is also symetric with respect
    to positive and negative categorization, however, there needs to be both
    positive and negative examples in the labels or it will always return 0.
    MCC of 0 is uncorrelated, 1 is completely correlated, and -1 is negatively correlated.

    .. math::

        \\text{MCC} = \\frac{ TP \\times TN - FP \\times FN }
        {\\sqrt{ (TP + FP) ( TP + FN ) ( TN + FP ) ( TN + FN ) } }

    where 0 terms in the denominator are replaced by 1.

    .. note::

        This version of MCC only supports binary classification.  See PCC.

    Parameters
    ----------
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.

    Examples
    --------
    >>> # In this example the network almost always predicts positive
    >>> false_positives = 1000
    >>> false_negatives = 1
    >>> true_positives = 10000
    >>> true_negatives = 1
    >>> predicts = [mx.np.array(
        [[.3, .7]]*false_positives +
        [[.7, .3]]*true_negatives +
        [[.7, .3]]*false_negatives +
        [[.3, .7]]*true_positives
    )]
    >>> labels  = [mx.np.array(
        [0.]*(false_positives + true_negatives) +
        [1.]*(false_negatives + true_positives)
    )]
    >>> f1 = mx.gluon.metric.F1()
    >>> f1.update(preds = predicts, labels = labels)
    >>> mcc = mx.gluon.metric.MCC()
    >>> mcc.update(preds = predicts, labels = labels)
    >>> f1.get()
    ('f1', 0.95233560306652054)
    >>> mcc.get()
    ('mcc', 0.01917751877733392)
    """

    def __init__(self, name='mcc',
                 output_names=None, label_names=None):
        self._metrics = _ClassificationMetrics()
        EvalMetric.__init__(self, name=name,
                            output_names=output_names, label_names=label_names)

    def update(self, labels, preds):
        """Updates the internal evaluation result.

        Parameters
        ----------
        labels : list of `NDArray`
            The labels of the data.

        preds : list of `NDArray`
            Predicted values.
        """
        labels, preds = check_label_shapes(labels, preds, True)

        for label, pred in zip(labels, preds):
            self._metrics.update_stats(label, pred)

        self.sum_metric = self._metrics.binary_matthewscc() * self._metrics.total_examples
        self.num_inst = self._metrics.total_examples

    def reset(self):
        """Resets the internal evaluation result to initial state."""
        self.sum_metric = 0.
        self.num_inst = 0.
        self._metrics.reset_stats()

####################
# REGRESSION METRICS
####################

@register
@use_np
class MAE(EvalMetric):
    """Computes Mean Absolute Error (MAE) loss.

    The mean absolute error is given by

    .. math::

        \\frac{\\sum_i^n |y_i - \\hat{y}_i|}{n}

    Parameters
    ----------
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.

    Examples
    --------
    >>> predicts = [mx.np.array([3, -0.5, 2, 7])]
    >>> labels = [mx.np.array([2.5, 0.0, 2, 8])]
    >>> mean_absolute_error = mx.gluon.metric.MAE()
    >>> mean_absolute_error.update(labels = labels, preds = predicts)
    >>> mean_absolute_error.get()
    ('mae', 0.5)
    """

    def __init__(self, name='mae',
                 output_names=None, label_names=None):
        super(MAE, self).__init__(
            name, output_names=output_names, label_names=label_names)

    def update(self, labels, preds):
        """Updates the internal evaluation result.

        Parameters
        ----------
        labels : list of `NDArray`
            The labels of the data.

        preds : list of `NDArray`
            Predicted values.
        """
        labels, preds = check_label_shapes(labels, preds, True)

        for label, pred in zip(labels, preds):
            label = label.as_np_ndarray()
            pred = pred.as_np_ndarray().to_device(label.device)

            num_inst = label.shape[0]
            mae = numpy.abs(label - pred).reshape(num_inst, -1).mean(axis=-1).sum()

            self.sum_metric += mae
            self.num_inst += num_inst

@register
@use_np
class MSE(EvalMetric):
    """Computes Mean Squared Error (MSE) loss.

    The mean squared error is given by

    .. math::

        \\frac{\\sum_i^n (y_i - \\hat{y}_i)^2}{n}

    Parameters
    ----------
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.

    Examples
    --------
    >>> predicts = [mx.np.array([3, -0.5, 2, 7])]
    >>> labels = [mx.np.array([2.5, 0.0, 2, 8])]
    >>> mean_squared_error = mx.gluon.metric.MSE()
    >>> mean_squared_error.update(labels = labels, preds = predicts)
    >>> mean_squared_error.get()
    ('mse', 0.375)
    """
    def __init__(self, name='mse',
                 output_names=None, label_names=None):
        super(MSE, self).__init__(
            name, output_names=output_names, label_names=label_names)

    def update(self, labels, preds):
        """Updates the internal evaluation result.

        Parameters
        ----------
        labels : list of `NDArray`
            The labels of the data.

        preds : list of `NDArray`
            Predicted values.
        """
        labels, preds = check_label_shapes(labels, preds, True)

        for label, pred in zip(labels, preds):
            label = label.as_np_ndarray()
            pred = pred.as_np_ndarray().to_device(label.device)

            num_inst = label.shape[0]
            mse = ((label - pred)**2.0).reshape(num_inst, -1).mean(axis=-1).sum()

            self.sum_metric += mse
            self.num_inst += num_inst

@register
@use_np
class RMSE(MSE):
    """Computes Root Mean Squred Error (RMSE) loss.

    The root mean squared error is given by

    .. math::

        \\sqrt{\\frac{\\sum_i^n (y_i - \\hat{y}_i)^2}{n}}

    Parameters
    ----------
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.

    Examples
    --------
    >>> predicts = [mx.np.array([3, -0.5, 2, 7])]
    >>> labels = [mx.np.array([2.5, 0.0, 2, 8])]
    >>> root_mean_squared_error = mx.gluon.metric.RMSE()
    >>> root_mean_squared_error.update(labels = labels, preds = predicts)
    >>> root_mean_squared_error.get()
    ('rmse', 0.612372457981)
    """
    def __init__(self, name='rmse',
                 output_names=None, label_names=None):
        super(RMSE, self).__init__(
            name, output_names=output_names, label_names=label_names)

    def get(self):
        if self.num_inst == 0:
            return (self.name, float('nan'))
        else:
            return (self.name, math.sqrt(self.sum_metric / self.num_inst))

@register
@use_np
class MeanPairwiseDistance(EvalMetric):
    """Computes Mean Pairwise Distance.

    The mean pairwise distance is given by

    .. math::

        \\sqrt{\\frac{(\\sum_i^n (y_i - \\hat{y}_i)^p)^\\frac{1}{p}}{n}}

    Parameters
    ----------
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.
    p : float, default 2
        calculating distance using the p-norm

    Examples
    --------
    >>> predicts = [mx.np.array([[1., 2.], [3., 4.]])]
    >>> labels = [mx.np.array([[1., 0.], [4., 2.]])]
    >>> mpd = mx.gluon.metric.MeanPairwiseDistance()
    >>> mpd.update(labels = labels, preds = predicts)
    >>> mpd.get()
    ('mpd', 2.1180338859558105)
    """
    def __init__(self, name='mpd',
                 output_names=None, label_names=None, p=2):
        super(MeanPairwiseDistance, self).__init__(
            name, output_names=output_names, label_names=label_names)
        self.p = p

    def update(self, labels, preds):
        """Updates the internal evaluation result.

        Parameters
        ----------
        labels : list of `NDArray`
            The labels of the data.

        preds : list of `NDArray`
            Predicted values.
        """
        labels, preds = check_label_shapes(labels, preds, True)

        for label, pred in zip(labels, preds):
            label = label.as_np_ndarray()
            pred = pred.as_np_ndarray().to_device(label.device)

            label = label.reshape(label.shape[0], -1)
            pred = pred.reshape(pred.shape[0], -1)

            dis = (((label - pred) ** self.p).sum(axis=-1)) ** (1./self.p)
            dis = dis.sum()
            num_inst = label.shape[0]

            self.sum_metric += dis
            self.num_inst += num_inst

@register
@use_np
class MeanCosineSimilarity(EvalMetric):
    r"""Computes Mean Cosine Similarity.

    The mean cosine similarity is given by

    .. math::

        cos_sim(label, pred) = \frac{{label}.{pred}}{max(||label||.||pred||, eps)}

    Calculation happens on the last dimension of label and pred.

    Parameters
    ----------
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.
    eps : float, default 1e-8
        small vale to avoid division by zero.

    Examples
    --------
    >>> predicts = [mx.np.array([[1., 0.], [1., 1.]])]
    >>> labels = [mx.np.array([[3., 4.], [2., 2.]])]
    >>> mcs = mx.gluon.metric.MeanCosineSimilarity()
    >>> mcs.update(labels = labels, preds = predicts)
    >>> mcs.get()
    ('cos_sim', 0.8)
    """
    def __init__(self, name='cos_sim',
                 output_names=None, label_names=None, eps=1e-8):
        super(MeanCosineSimilarity, self).__init__(
            name, output_names=output_names, label_names=label_names)
        self.eps = eps

    def update(self, labels, preds):
        """Updates the internal evaluation result.

        Parameters
        ----------
        labels : list of `NDArray`
            The labels of the data.

        preds : list of `NDArray`
            Predicted values.
        """
        labels, preds = check_label_shapes(labels, preds, True)

        for label, pred in zip(labels, preds):
            label = label.as_np_ndarray()
            pred = pred.as_np_ndarray().to_device(label.device)

            if len(label.shape) == 1:
                label = label.reshape(1, label.shape[0])
            if len(pred.shape) == 1:
                pred = pred.reshape(1, pred.shape[0])

            sim = (label * pred).sum(axis=-1)
            n_p = numpy.linalg.norm(pred, axis=-1)
            n_l = numpy.linalg.norm(label, axis=-1)
            sim = sim / numpy.maximum(n_l * n_p, self.eps)
            sim = sim.sum()
            num_inst = len(label.reshape(-1, label.shape[-1])) # numpy.prod(label.shape[:-1]) is not supported
            self.sum_metric += sim
            self.num_inst += num_inst

@register
@alias('ce')
@use_np
class CrossEntropy(EvalMetric):
    """Computes Cross Entropy loss.

    The cross entropy over a batch of sample size :math:`N` is given by

    .. math::

       -\\sum_{n=1}^{N}\\sum_{k=1}^{K}t_{nk}\\log (y_{nk}),

    where :math:`t_{nk}=1` if and only if sample :math:`n` belongs to class :math:`k`.
    :math:`y_{nk}` denotes the probability of sample :math:`n` belonging to
    class :math:`k`.

    Parameters
    ----------
    eps : float, default 1e-12
        Use small constant for the case that predicted value is 0.
    ignore_label : int or None, default None
        Index of invalid label to ignore when
        counting. By default, sets to -1.
        If set to `None`, it will include all entries.
    axis : int, default -1
        The axis from prediction that was used to
        compute softmax. By default use the last axis.
    from_logits : boolean, default False
        Whether `pred` is expected to be a logits tensor.
        By default, we assume that `pred` encodes a probability distribution.
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.

    Examples
    --------
    >>> predicts = [mx.np.array([[0.3, 0.7], [0, 1.], [0.4, 0.6]])]
    >>> labels   = [mx.np.array([0, 1, 1])]
    >>> ce = mx.gluon.metric.CrossEntropy()
    >>> ce.update(labels, predicts)
    >>> ce.get()
    ('cross-entropy', 0.57159948348999023)
    """
    def __init__(self, eps=1e-12, ignore_label=None, axis=-1, from_logits=False,
                 name='cross-entropy', output_names=None, label_names=None):
        super(CrossEntropy, self).__init__(
            name, output_names=output_names, label_names=label_names)
        self.ignore_label = ignore_label
        self.axis = axis
        self.from_logits = from_logits
        self.eps = eps

    def update(self, labels, preds):
        """Updates the internal evaluation result.

        Parameters
        ----------
        labels : list of `NDArray`
            The labels of the data.

        preds : list of `NDArray`
            Predicted values.
        """
        labels, preds = check_label_shapes(labels, preds, True)

        loss = 0.
        num = 0
        for label, pred in zip(labels, preds):
            assert label.size == pred.size/pred.shape[-1], \
                f"shape mismatch: {label.shape} vs. {pred.shape}"
            label = label.reshape((label.size,))
            if self.from_logits:
                pred = npx.softmax(pred, axis=self.axis)
            pred = npx.pick(pred.to_device(label.device), label.astype(dtype='int32'), axis=self.axis)
            if self.ignore_label is not None:
                ignore = (label == self.ignore_label).astype(pred.dtype)
                num -= ignore.sum()
                pred = pred * (1 - ignore) + ignore
            loss -= numpy.log(numpy.maximum(self.eps, pred)).sum()
            num += pred.size
        self.sum_metric += loss
        self.num_inst += num

@register
@use_np
class Perplexity(CrossEntropy):
    """Computes perplexity.

    Perplexity is a measurement of how well a probability distribution
    or model predicts a sample. A low perplexity indicates the model
    is good at predicting the sample.

    The perplexity of a model q is defined as

    .. math::

        b^{\\big(-\\frac{1}{N} \\sum_{i=1}^N \\log_b q(x_i) \\big)}
        = \\exp \\big(-\\frac{1}{N} \\sum_{i=1}^N \\log q(x_i)\\big)

    where we let `b = e`.

    :math:`q(x_i)` is the predicted value of its ground truth
    label on sample :math:`x_i`.

    For example, we have three samples :math:`x_1, x_2, x_3` and their labels
    are :math:`[0, 1, 1]`.
    Suppose our model predicts :math:`q(x_1) = p(y_1 = 0 | x_1) = 0.3`
    and :math:`q(x_2) = 1.0`,
    :math:`q(x_3) = 0.6`. The perplexity of model q is
    :math:`exp\\big(-(\\log 0.3 + \\log 1.0 + \\log 0.6) / 3\\big) = 1.77109762852`.

    Parameters
    ----------
    eps : float, default 1e-12
        Use small constant for the case that predicted value is 0.
    ignore_label : int or None, default None
        Index of invalid label to ignore when
        counting. By default, sets to -1.
        If set to `None`, it will include all entries.
    axis : int (default -1)
        The axis from prediction that was used to
        compute softmax. By default use the last axis.
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.

    Examples
    --------
    >>> predicts = [mx.np.array([[0.3, 0.7], [0, 1.], [0.4, 0.6]])]
    >>> labels   = [mx.np.array([0, 1, 1])]
    >>> perp = mx.gluon.metric.Perplexity(ignore_label=None)
    >>> perp.update(labels, predicts)
    >>> perp.get()
    ('Perplexity', 1.7710976285155853)
    """
    def __init__(self, eps=1e-12, ignore_label=None, axis=-1, from_logits=False,
                 name='perplexity', output_names=None, label_names=None):
        super(Perplexity, self).__init__(
            eps=eps, ignore_label=ignore_label, axis=axis, from_logits=from_logits,
            name=name, output_names=output_names, label_names=label_names)

    def get(self):
        if self.num_inst == 0:
            return (self.name, float('nan'))
        else:
            return (self.name, math.exp(self.sum_metric/self.num_inst))

@register
@alias('pearsonr')
@use_np
class PearsonCorrelation(EvalMetric):
    """Computes Pearson correlation.

    The pearson correlation is given by

    .. math::

        \\frac{cov(y, \\hat{y})}{\\sigma{y}\\sigma{\\hat{y}}}

    Parameters
    ----------
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.

    Examples
    --------
    >>> predicts = [mx.np.array([[0.3, 0.7], [0, 1.], [0.4, 0.6]])]
    >>> labels   = [mx.np.array([[1, 0], [0, 1], [0, 1]])]
    >>> pr = mx.gluon.metric.PearsonCorrelation()
    >>> pr.update(labels, predicts)
    >>> pr.get()
    ('pearsonr', 0.42163704544016178)
    """
    def __init__(self, name='pearsonr',
                 output_names=None, label_names=None):
        super(PearsonCorrelation, self).__init__(
            name, output_names=output_names, label_names=label_names)
        self.reset()

    def reset(self):
        self._sse_p = 0
        self._mean_p = 0
        self._sse_l = 0
        self._mean_l = 0
        self._pred_nums = 0
        self._label_nums = 0
        self._conv = 0

        self.num_inst = 0
        self.sum_metric = 0.0

    def update_variance(self, new_values, *aggregate):
        #Welford's online algorithm for variance update
        count, mean, m_2 = aggregate
        count += len(new_values)
        delta = new_values - mean
        mean += numpy.sum(delta / count)
        delta_2 = new_values - mean
        m_2 += numpy.sum(delta * delta_2)
        return count, mean, m_2

    def update_cov(self, label, pred):
        self._conv = self._conv + numpy.sum((label - self._mean_l) * (pred - self._mean_p))

    def update(self, labels, preds):
        """Updates the internal evaluation result.

        Parameters
        ----------
        labels : list of `NDArray`
            The labels of the data.
        preds : list of `NDArray`
            Predicted values.
        """
        labels, preds = check_label_shapes(labels, preds, True)
        for label, pred in zip(labels, preds):
            check_label_shapes(label, pred, False, True)
            label = label.as_np_ndarray().reshape(-1).astype(numpy.float64)
            pred = pred.as_np_ndarray().to_device(label.device).reshape(-1).astype(numpy.float64)

            self.num_inst += 1
            self._label_nums, self._mean_l, self._sse_l = \
                self.update_variance(label, self._label_nums, self._mean_l, self._sse_l)
            self.update_cov(label, pred)
            self._pred_nums, self._mean_p, self._sse_p = \
                self.update_variance(pred, self._pred_nums, self._mean_p, self._sse_p)

    def get(self):
        if self.num_inst == 0:
            return (self.name, float('nan'))

        n = self._label_nums
        pearsonr = self._conv / ((n-1) * numpy.sqrt(self._sse_p / (n - 1)) * numpy.sqrt(self._sse_l / (n - 1)))
        return (self.name, float(pearsonr))

@register
@use_np
class PCC(EvalMetric):
    """PCC is a multiclass equivalent for the Matthews correlation coefficient derived
    from a discrete solution to the Pearson correlation coefficient.

    .. math::

        \\text{PCC} = \\frac {\\sum _{k}\\sum _{l}\\sum _{m}C_{kk}C_{lm}-C_{kl}C_{mk}}
        {{\\sqrt {\\sum _{k}(\\sum _{l}C_{kl})(\\sum _{k'|k'\\neq k}\\sum _{l'}C_{k'l'})}}
         {\\sqrt {\\sum _{k}(\\sum _{l}C_{lk})(\\sum _{k'|k'\\neq k}\\sum _{l'}C_{l'k'})}}}

    defined in terms of a K x K confusion matrix C.

    When there are more than two labels the PCC will no longer range between -1 and +1.
    Instead the minimum value will be between -1 and 0 depending on the true distribution.
    The maximum value is always +1.

    Parameters
    ----------
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.

    Examples
    --------
    >>> # In this example the network almost always predicts positive
    >>> false_positives = 1000
    >>> false_negatives = 1
    >>> true_positives = 10000
    >>> true_negatives = 1
    >>> predicts = [mx.np.array(
        [[.3, .7]]*false_positives +
        [[.7, .3]]*true_negatives +
        [[.7, .3]]*false_negatives +
        [[.3, .7]]*true_positives
    )]
    >>> labels  = [mx.np.array(
        [0]*(false_positives + true_negatives) +
        [1]*(false_negatives + true_positives)
    )]
    >>> f1 = mx.gluon.metric.F1()
    >>> f1.update(preds = predicts, labels = labels)
    >>> pcc = mx.gluon.metric.PCC()
    >>> pcc.update(preds = predicts, labels = labels)
    >>> f1.get()
    ('f1', 0.95233560306652054)
    >>> pcc.get()
    ('pcc', 0.01917751877733392)
    """
    def __init__(self, name='pcc',
                 output_names=None, label_names=None):
        self.k = 2
        super(PCC, self).__init__(
            name=name, output_names=output_names, label_names=label_names)

    def _grow(self, inc):
        self.lcm = numpy.pad(
            self.lcm, ((0, inc), (0, inc)), 'constant', constant_values=(0))
        self.k += inc

    def _calc_mcc(self, cmat):
        n = cmat.sum()
        x = cmat.sum(axis=1)
        y = cmat.sum(axis=0)
        cov_xx = numpy.sum(x * (n - x))
        cov_yy = numpy.sum(y * (n - y))
        if cov_xx == 0 or cov_yy == 0:
            return float('nan')
        # i = cmat.diagonal() # mxnet.numpy.ndarray.diagonal() is currently not available.
        i = cmat[numpy.arange(self.k), numpy.arange(self.k)]
        cov_xy = numpy.sum(i * n - x * y)
        return cov_xy / (cov_xx * cov_yy) ** 0.5

    def update(self, labels, preds):
        """Updates the internal evaluation result.

        Parameters
        ----------
        labels : list of `NDArray`
            The labels of the data.

        preds : list of `NDArray`
            Predicted values.
        """
        labels, preds = check_label_shapes(labels, preds, True)

        # update the confusion matrix
        for label, pred in zip(labels, preds):
            label = label.astype('int32', copy=False).as_np_ndarray()
            pred = pred.as_np_ndarray().to_device(label.device)
            if pred.shape != label.shape:
                pred = pred.argmax(axis=1).astype(label, copy=False)
            else:
                pred = pred.astype('int32', copy=False)
            n = int(max(pred.max(), label.max()))
            if n >= self.k:
                self._grow(n + 1 - self.k)
            bcm = numpy.zeros((self.k, self.k), dtype='float64')
            for i, j in zip(pred, label):
                bcm[i, j] += 1
            self.lcm += bcm
        self.num_inst += 1

    @property
    def sum_metric(self):
        return self._calc_mcc(self.lcm) * self.num_inst

    def reset(self):
        """Resets the internal evaluation result to initial state."""
        self.num_inst = 0.
        self.lcm = numpy.zeros((self.k, self.k), dtype='float64')

@register
@use_np
class Loss(EvalMetric):
    """Dummy metric for directly printing loss.

    Parameters
    ----------
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.
    """
    def __init__(self, name='loss',
                 output_names=None, label_names=None):
        super(Loss, self).__init__(
            name, output_names=output_names, label_names=label_names)

    def update(self, _, preds):

        if isinstance(preds, ndarray.ndarray.NDArray):
            preds = [preds]

        for pred in preds:
            loss = pred.sum().item()
            self.sum_metric += loss
            self.num_inst += pred.size

@register
class Torch(Loss):
    """Dummy metric for torch criterions."""
    def __init__(self, name='torch',
                 output_names=None, label_names=None):
        super(Torch, self).__init__(
            name, output_names=output_names, label_names=label_names)

@register
@use_np
class CustomMetric(EvalMetric):
    """Computes a customized evaluation metric.

    The `feval` function can return a `tuple` of (sum_metric, num_inst) or return
    an `int` sum_metric.

    Parameters
    ----------
    feval : callable(label, pred)
        Customized evaluation function.
    name : str, optional
        The name of the metric. (the default is None).
    allow_extra_outputs : bool, optional
        If true, the prediction outputs can have extra outputs.
        This is useful in RNN, where the states are also produced
        in outputs for forwarding. (the default is False).
    name : str
        Name of this metric instance for display.
    output_names : list of str, or None
        Name of predictions that should be used when updating with update_dict.
        By default include all predictions.
    label_names : list of str, or None
        Name of labels that should be used when updating with update_dict.
        By default include all labels.

    Examples
    --------
    >>> predicts = [mx.np.array(np.array([3, -0.5, 2, 7]).reshape(4,1))]
    >>> labels = [mx.np.array(np.array([2.5, 0.0, 2, 8]).reshape(4,1))]
    >>> feval = lambda x, y : (x + y).mean()
    >>> eval_metrics = mx.gluon.metric.CustomMetric(feval=feval)
    >>> eval_metrics.update(labels, predicts)
    >>> eval_metrics.get()
    ('custom(<lambda>)', 6.0)
    """
    def __init__(self, feval, name=None, allow_extra_outputs=False,
                 output_names=None, label_names=None):
        if name is None:
            name = feval.__name__
            if name.find('<') != -1:
                name = f'custom({name})'
        super(CustomMetric, self).__init__(
            name, feval=feval,
            allow_extra_outputs=allow_extra_outputs,
            output_names=output_names, label_names=label_names)
        self._feval = feval
        self._allow_extra_outputs = allow_extra_outputs

    def update(self, labels, preds):
        """Updates the internal evaluation result.

        Parameters
        ----------
        labels : list of `NDArray`
            The labels of the data.

        preds : list of `NDArray`
            Predicted values.
        """
        if not self._allow_extra_outputs:
            labels, preds = check_label_shapes(labels, preds, True)

        for pred, label in zip(preds, labels):
            label = label.as_np_ndarray()
            pred = pred.as_np_ndarray().to_device(label.device)

            reval = self._feval(label, pred)
            if isinstance(reval, tuple):
                (sum_metric, num_inst) = reval
                self.sum_metric += sum_metric
                self.num_inst += num_inst
            else:
                self.sum_metric += reval
                self.num_inst += 1

    def get_config(self):
        raise NotImplementedError("CustomMetric cannot be serialized")

# pylint: disable=invalid-name
def np(numpy_feval, name=None, allow_extra_outputs=False):
    """Creates a custom evaluation metric that receives its inputs as numpy arrays.

    Parameters
    ----------
    numpy_feval : callable(label, pred)
        Custom evaluation function that receives labels and predictions for a minibatch
        as numpy arrays and returns the corresponding custom metric as a floating point number.
    name : str, optional
        Name of the custom metric.
    allow_extra_outputs : bool, optional
        Whether prediction output is allowed to have extra outputs. This is useful in cases
        like RNN where states are also part of output which can then be fed back to the RNN
        in the next step. By default, extra outputs are not allowed.

    Returns
    -------
    float
        Custom metric corresponding to the provided labels and predictions.

    Example
    -------
    >>> def custom_metric(label, pred):
    ...     return np.mean(np.abs(label-pred))
    ...
    >>> metric = mx.gluon.metric.np(custom_metric)
    """
    def feval(label, pred):
        """Internal eval function."""
        return numpy_feval(label, pred)
    feval.__name__ = numpy_feval.__name__
    return CustomMetric(feval, name, allow_extra_outputs)
# pylint: enable=invalid-name


#!/usr/bin/python
import sqlite3
from flask import Flask, request, jsonify
from flask_cors import CORS
# from flask_restful import APIException
from flask import abort

def connect_to_db():
    conn = sqlite3.connect('my_database.db')
    print("open database successfully")
    return conn

def create_db_table():
    try:
        conn = connect_to_db()
        c = conn.cursor()
        tables = c.execute("""SELECT name FROM sqlite_master WHERE type='table' AND name='users'""").fetchall()
        if tables != []:
            conn.execute('''DROP TABLE users''')
        conn.execute('''
            CREATE TABLE users (
                player_id INTEGER PRIMARY KEY NOT NULL,
                username TEXT NOT NULL,
                xp INTEGER DEFAULT 0 ,
                gold INTEGER DEFAULT 0
            )
        ''')

        conn.commit()
        print("User table created successfully")
    except:
        print("User table creation failed - Maybe table")
    finally:
        conn.close()

def insert_user(user):
    inserted_user = {}
    try:
        conn = connect_to_db()
        cur = conn.cursor()
        cur.execute("INSERT INTO users (username, xp, gold) VALUES (?, ?, ?)", (user['username'], 0,0) )
        conn.commit()
        # inserted_user = get_user_by_id(cur.lastrowid)
        player_id = cur.lastrowid
        cur.execute("SELECT username, player_id FROM users WHERE player_id = ?", (player_id,))
        row = cur.fetchone()
        inserted_user["username"] = row[0]
        inserted_user["player_id"] = row[1]
    except:
        conn().rollback()

    finally:
        conn.close()

    return inserted_user

def get_users():
    users = []
    try:
        conn = connect_to_db()
        conn.row_factory = sqlite3.Row
        cur = conn.cursor()
        cur.execute("SELECT * FROM users")
        rows = cur.fetchall()

        # convert row objects to dictionary
        for i in rows:
            user = {}
            user["player_id"] = i["player_id"]
            user["username"] = i["username"]
            user["xp"] = i["xp"]
            user["gold"] = i["gold"]
            users.append(user)

    except:
        users = []

    return users

def get_user_by_id(player_id):
    user = {}
    try:
        conn = connect_to_db()
        conn.row_factory = sqlite3.Row
        cur = conn.cursor()
        cur.execute("SELECT * FROM users WHERE player_id = ?", (player_id,))
        row = cur.fetchone()

        # convert row object to dictionary
        user["player_id"] = row["player_id"]
        user["username"] = row["username"]
        user["xp"] = row["xp"]
        user["gold"] = row["gold"]
    except:
        user = {}

    return user

def update_user(user, player_id):
    updated_user = {}
    try:
        conn = connect_to_db()
        cur = conn.cursor()
        cur.execute("UPDATE users SET username = ?, xp = ?, gold = ? WHERE player_id =?", (user["username"], user["xp"], user["gold"], player_id,))
        conn.commit()
        #return the user
        updated_user = get_user_by_id(player_id)

    except:
        conn.rollback()
        # updated_user = {}
    finally:
        conn.close()

    return updated_user

def leaderboard(sortby,size):
    users = []
    try:
        conn = connect_to_db()
        conn.row_factory = sqlite3.Row
        cur = conn.cursor()
        cur.execute("SELECT * from users ORDER BY {} DESC LIMIT {}".format(sortby,size))
        rows = cur.fetchall()
        for i in rows:
            user = {}
            user['username'] = i['username']
            user['player_id'] = i['player_id']
            user['gold'] = i['gold']
            user['xp'] = i['xp']
            users.append(user)
    except:
        users = []
    return users

users = []
user0 = {
    "username": "Mayank"
}

user1 = {
    "username": "Garg"
}

user2 = {
    "username": "Hogwarts"
}

user3 = {
    "username": "Gryffindor"
}

users.append(user0)
users.append(user1)
users.append(user2)
users.append(user3)

create_db_table()
# delete_db_table()
for i in users:
    print(insert_user(i))

app = Flask(__name__)
CORS(app, resources={r"/*": {"origins": "*"}})

@app.route('/api/player', methods=['GET'])
def api_get_users():
    return jsonify(get_users())

@app.route('/api/player/<player_id>', methods=['GET'])
def api_get_user(player_id):
    out_json = jsonify(get_user_by_id(player_id))
    if not out_json.json:
        return "Error 405: Player_id not found!"
    return out_json

@app.route('/api/v1/player',  methods = ['POST'])
def api_add_user():
    user = request.get_json()
    out_json = jsonify(insert_user(user))
    if not out_json.json:
        return "Error 405: Provide Username to create a new player in JSON!"
    return out_json

@app.route('/api/player/<player_id>',  methods = ['PUT'])
def api_update_user(player_id):
    user = request.get_json()
    out_json = jsonify(update_user(user,player_id))
    if not out_json.json:
        return "Error 405: Provide Username, player_id, xp and Gold in JSON to update the player's information!"
    return out_json

@app.route('/api/leaderboards', methods = ['GET'])
def api_leaderboard():
    args = request.args
    sortby = args.get('sortby')
    size = args.get('size')
    out_json = jsonify(leaderboard(sortby,size))
    if not out_json.json:
        return "Error 405: Provide valid Sortby:gold/xp and size as parameter!"
    return out_json

@app.route('/')
def index():
    return "Hello Players!"

@app.errorhandler(Exception)
def basic_error(e):
    return "an error occured: " + str(e)

if __name__ == "__main__":
    #app.debug = True
    # app.run(host="127.0.0.1")
    app.run(host="0.0.0.0", port=80)


from brownie import FundMe
from scripts.helpful_scripts import get_account
import pytest

def fund():
    fund_me = FundMe[-1]
    account = get_account()
    entrance_fee = fund_me.getEntranceFee()

    print(f"Current entry free is:{entrance_fee}") #you should get 2500001
    print("funding...")
    fund_me.fund({"from":account, "value": entrance_fee})
    print("Funded...")

def withdraw():
    fund_me = FundMe[-1]
    account = get_account()

    fund_me.withdraw({"from":account})
    print("withdrawed.....")

def main():
    fund()
    withdraw()


import paddle
import paddle.nn as nn
import paddle.nn.functional as F

from models.submodules import feature_extraction, post_3dconvs, refinement1, refinement2

class LWSNet(nn.Layer):
    def __init__(self, args):
        super(LWSNet, self).__init__()

        self.maxdisplist = args.maxdisplist
        self.layers_3d = args.layers_3d
        self.channels_3d = args.channels_3d
        self.growth_rate = args.growth_rate

        self.feature_extraction = feature_extraction()
        self.volume_postprocess = []

        for i in range(3):
            net3d = post_3dconvs(self.layers_3d, self.channels_3d*self.growth_rate[i])
            self.volume_postprocess.append(net3d)
        self.volume_postprocess = nn.LayerList(self.volume_postprocess) #3D CNN in Stage 1 to Stage 3

        self.refinement1_left = refinement1(in_channels=3, out_channels=32) #input: left image output: left features
        self.refinement1_disp = refinement1(in_channels=1, out_channels=32) #input: disparity stage 3 output: disparity features
        self.refinement2 = refinement2(in_channels=64, out_channels=32)

    def warp(self, x, disp):
        """
        warp an image/tensor (im2) back to im1, according to the optical flow
        x: [B, C, H, W] (im2)
        disp: [B, 1, H, W]
        flo: [B, 2, H, W] flow
        output: [B, C, H, W] (im1)
        """
        B, C, H, W = x.shape
        # mesh grid
        xx = paddle.expand(paddle.arange(0, W, step=1, dtype='float32').reshape(shape=[1, -1]), shape=[H, W])
        yy = paddle.expand(paddle.arange(0, H, step=1, dtype='float32').reshape(shape=[-1, 1]), shape=[H, W])

        xx = paddle.expand(xx.reshape(shape=[1, 1, H, W]), shape=[B, 1, H, W])
        yy = paddle.expand(yy.reshape(shape=[1, 1, H, W]), shape=[B, 1, H, W])

        vgrid = paddle.concat((xx, yy), axis=1) #[B, 2, H, W]
        vgrid[:, :1, :, :] = vgrid[:, :1, :, :] - disp
        # scale grid to [-1,1]
        vgrid[:, 0, :, :] = 2.0 * vgrid[:, 0, :, :] / max(W - 1, 1) - 1.0
        vgrid[:, 1, :, :] = 2.0 * vgrid[:, 1, :, :] / max(H - 1, 1) - 1.0

        vgrid = paddle.transpose(vgrid, [0, 2, 3, 1]) #[B, H, W, 2]
        vgrid.stop_gradient = False

        output = F.grid_sample(x, vgrid)

        return output

    def _build_volume_2d(self, feat_l, feat_r, maxdisp, stride=1):
        """
        output full disparity map
        L1 distance-based cost
        """
        assert maxdisp % stride == 0

        cost = paddle.zeros((feat_l.shape[0], maxdisp // stride, feat_l.shape[2], feat_l.shape[3]), dtype='float32')
        cost.stop_gradient=False

        for i in range(0, maxdisp, stride):

            if i > 0:
                cost[:, i // stride, :, :i] = feat_l[:, :, :, :i].abs().sum(axis=1) #occlusion regions
                cost[:, i // stride, :, i:] = paddle.norm(feat_l[:, :, :, i:] - feat_r[:, :, :, :-i], 1, 1)
            else:
                cost[:, i // stride, :, i:] = paddle.norm(feat_l[:, :, :, :] - feat_r[:, :, :, :], 1, 1)

        return cost

    def _build_volume_2d3(self, feat_l, feat_r, maxdisp, disp, stride=1):
        """
        output residual map
        L1 distance-based cost
        """
        size = feat_l.shape

        disp = paddle.unsqueeze(disp, axis=1)
        batch_disp = paddle.expand(disp, shape=[disp.shape[0], maxdisp * 2 - 1, disp.shape[-3], disp.shape[-2],
                                                disp.shape[-1]])
        batch_disp = batch_disp.reshape(shape=[-1, 1, size[-2], size[-1]])

        batch_shift = paddle.arange(-maxdisp + 1, maxdisp, dtype="float32")
        batch_shift = paddle.expand(batch_shift, shape=[size[0], batch_shift.shape[0]]).reshape(shape=[-1]).unsqueeze(
            axis=[1, 2, 3]) * stride
        batch_disp = batch_disp - batch_shift
        batch_feat_l = paddle.unsqueeze(feat_l, axis=1).expand(
            shape=[size[0], maxdisp * 2 - 1, size[-3], size[-2], size[-1]]).reshape(
            shape=[-1, size[-3], size[-2], size[-1]])
        batch_feat_r = paddle.unsqueeze(feat_r, axis=1).expand(
            shape=[size[0], maxdisp * 2 - 1, size[-3], size[-2], size[-1]]).reshape(
            shape=[-1, size[-3], size[-2], size[-1]])

        cost = paddle.norm(batch_feat_l - self.warp(batch_feat_r, batch_disp), 1, 1) #output residual map
        cost = cost.reshape(shape=[size[0], -1, size[2], size[3]])

        return cost

    def forward(self, left_input, right_input):

        img_size = left_input.shape

        feats_l = self.feature_extraction(left_input) #left features
        feats_r = self.feature_extraction(right_input) #right features

        pred = []

        for scale in range(len(feats_l)):

            if scale > 0:
                #stage 2 and stage 3
                wflow = F.interpolate(pred[scale - 1], size=[feats_l[scale].shape[2], feats_l[scale].shape[3]],
                                      mode="bilinear") * \
                        feats_l[scale].shape[2] / img_size[2] #resize disparity of last stage to current resolution

                cost = self._build_volume_2d3(feats_l[scale],
                                              feats_r[scale],
                                              self.maxdisplist[scale],
                                              wflow,
                                              stride=1) #build cost volume

            else:
                #stage 1
                cost = self._build_volume_2d(feats_l[scale],
                                             feats_r[scale],
                                             self.maxdisplist[scale],
                                             stride=1) #build cost volume

            cost = paddle.unsqueeze(cost, [1])
            cost = self.volume_postprocess[scale](cost) + cost #3D CNN, skip connection
            cost = paddle.squeeze(cost, [1])

            if scale == 0:
                #stage 1
                pre_low_res = disparity_regression(start=0, end=self.maxdisplist[0])(input=F.softmax(-cost, axis=1)) #full disparity
                #softmax function computes the probability of a pixel's disparity to be d
                #'softmax(cost)' or 'softmax(-cost)' do not affect the performance because feature-based cost volume provided flexibility.
                pre_low_res = pre_low_res * img_size[2] / pre_low_res.shape[2] #transform disparity value to original resolution
                disp_up = F.interpolate(pre_low_res, size=[img_size[2], img_size[3]], mode="bilinear") #upsample to original resolution

                pred.append(disp_up)
            else:
                #stage 2 and 3
                pre_low_res = disparity_regression(start=-self.maxdisplist[scale] + 1,
                                                   end=self.maxdisplist[scale])(input=F.softmax(-cost, axis=1)) #residual
                pre_low_res = pre_low_res * img_size[2] / pre_low_res.shape[2] #transform residual value to original resolution
                disp_up = F.interpolate(pre_low_res, size=[img_size[2], img_size[3]], mode="bilinear") #upsample to original resolution

                pred.append(disp_up + pred[scale - 1]) #skip connection

        refined_left = self.refinement1_left(left_input)
        refined_disp = self.refinement1_disp(pred[-1])
        disp = self.refinement2(input=paddle.concat([refined_left, refined_disp], 1))
        disp_up = F.interpolate(disp, size=[img_size[2], img_size[3]], mode="bilinear")
        pred.append(pred[2] + disp_up) #skip connection

        return pred #disparity maps of 4 stages

class disparity_regression(nn.Layer):
    def __init__(self, start, end, stride=1):
        super(disparity_regression, self).__init__()
        self.disp = paddle.arange(start * stride, end * stride, stride, dtype='float32')
        self.disp.stop_gradient = True
        self.disp = paddle.reshape(self.disp, shape=[1, -1, 1, 1])
        _, self.my_steplength, _, _ = self.disp.shape

    def forward(self, input):

        disp = paddle.expand(self.disp, (input.shape[0], self.my_steplength, input.shape[2], input.shape[3]))
        output = paddle.sum(input * disp, axis=1, keepdim=True) #compute expectation
        return output


# -*- coding: utf-8 -*-
"""
主控制程序入口
"""
import traceback
import sys,os,logging
from Functions import *

action_dict = dict()

def start_action(action_list, action_args = {}, web = False):
	for action in action_list:
		if action in action_args.keys():
			print(action_args[action])
			actionInstance = A(action, **action_args[action])
		else:
			actionInstance = A(action)
		action_dict[action] = actionInstance
	for action in action_list:
		if web == True:
			action_dict[action].setDaemon(True)
		action_dict[action].start()

	if web == False:
		for action in action_list:
			action_dict[action].join()

if __name__ == "__main__":
	logging.basicConfig(level=logging.ERROR)
	actionList = ['PrintSinaFQ','PrintSinaL2']
	start_action(actionList)