# -*- coding: utf-8 -*-
"""
TRABAJO 2
Nombre Estudiante: Alejandro Palencia Blanco
"""
import numpy as np
import matplotlib.pyplot as plt

# Fijamos la semilla
np.random.seed(1)

def simula_unif(N, dim, rango):
	return np.random.uniform(rango[0],rango[1],(N,dim))

def simula_gauss(N, dim, sigma):
    media = 0
    out = np.zeros((N,dim),np.float64)
    for i in range(N):
        # Para cada columna dim se emplea un sigma determinado. Es decir, para
        # la primera columna (eje X) se usará una N(0,sqrt(sigma[0]))
        # y para la segunda (eje Y) N(0,sqrt(sigma[1]))
        out[i,:] = np.random.normal(loc=media, scale=np.sqrt(sigma), size=dim)

    return out

def simula_recta(intervalo):
    points = np.random.uniform(intervalo[0], intervalo[1], size=(2, 2))
    x1 = points[0,0]
    x2 = points[1,0]
    y1 = points[0,1]
    y2 = points[1,1]
    # y = a*x + b
    a = (y2-y1)/(x2-x1) # Calculo de la pendiente.
    b = y1 - a*x1       # Calculo del termino independiente.

    return a, b

# EJERCICIO 1.1: Dibujar una gráfica con la nube de puntos de salida correspondiente

x_unif = simula_unif(50, 2, [-50,50])

plt.scatter(x_unif[:,0], x_unif[:,1])
plt.title("Nube de puntos uniforme")
plt.show()

input("\n--- Pulsar tecla para continuar ---\n")

x_gauss = simula_gauss(50, 2, np.array([5,7]))

plt.scatter(x_gauss[:,0], x_gauss[:,1])
plt.title("Nube de puntos gaussiana")
plt.show()

input("\n--- Pulsar tecla para continuar ---\n")

###############################################################################
###############################################################################
###############################################################################

# EJERCICIO 1.2: Dibujar una gráfica con la nube de puntos de salida correspondiente

# La funcion np.sign(0) da 0, lo que nos puede dar problemas
def signo(x):
	if x >= 0:
		return 1
	return -1

def f(x, y, a, b):
	return signo(y - a*x - b)

# Función que dibuja una nube de puntos etiquetada junto con una recta en los límites especificados
def dibujar_nube_etiq_con_recta(x, y, a, b, etiq1, etiq1neg, x_inf, x_sup, y_inf, y_sup, title=''):
    x_label1 = x[np.where(y == 1)]
    plt.scatter(x_label1[:,0], x_label1[:,1], c="blue", s=5, label=etiq1)
    x_label1neg = x[np.where(y == -1)]
    plt.scatter(x_label1neg[:,0], x_label1neg[:,1], c="red", s=5, label=etiq1neg)
    t = np.linspace(x_inf,x_sup,2)
    plt.plot(t, a*t+b, c="green")
    plt.ylim(y_inf,y_sup)
    plt.title(title)
    plt.legend()
    plt.show()

a, b = simula_recta([-50,50])
x = simula_unif(100, 2, [-50,50])
y = np.array([f(x[i][0],x[i][1],a,b) for i in range(x.shape[0])])

dibujar_nube_etiq_con_recta(x, y, a, b, 1, -1, -50, 50, -50, 50,
                            "Nube de puntos uniforme etiquetada")

input("\n--- Pulsar tecla para continuar ---\n")

# 1.2.b. Dibujar una gráfica donde los puntos muestren el resultado de su etiqueta, junto con la recta usada para ello
# Array con 10% de indices aleatorios para introducir ruido

# Para cada etiqueta, tomo los índices de los elementos con esa etiqueta,
#   los barajo y cambio el signo de la primera décima parte de ellos
y_ruido = np.copy(y)
for label in (1,-1):
    elem_label = np.where(y_ruido == label)[0]
    for elem in np.random.permutation(elem_label)[0:elem_label.size//10+1]:
        y_ruido[elem] *= -1

dibujar_nube_etiq_con_recta(x, y_ruido, a, b, 1, -1, -50, 50, -50, 50,
                            "Nube de puntos uniforme etiquetada con un 10% de ruido")

input("\n--- Pulsar tecla para continuar ---\n")

###############################################################################
###############################################################################
###############################################################################

# EJERCICIO 1.3: Supongamos ahora que las siguientes funciones definen la frontera de clasificación de los puntos de la muestra en lugar de una recta

def plot_datos_cuad(X, y, fz, title='Point cloud plot', xaxis='x axis', yaxis='y axis'):
    #Preparar datos
    min_xy = X.min(axis=0)
    max_xy = X.max(axis=0)
    border_xy = (max_xy-min_xy)*0.01

    #Generar grid de predicciones
    xx, yy = np.mgrid[min_xy[0]-border_xy[0]:max_xy[0]+border_xy[0]+0.001:border_xy[0],
                      min_xy[1]-border_xy[1]:max_xy[1]+border_xy[1]+0.001:border_xy[1]]
    grid = np.c_[xx.ravel(), yy.ravel(), np.ones_like(xx).ravel()]
    pred_y = fz(grid)
    # pred_y[(pred_y>-1) & (pred_y<1)]
    pred_y = np.clip(pred_y, -1, 1).reshape(xx.shape)

    #Plot
    f, ax = plt.subplots(figsize=(8, 6))
    contour = ax.contourf(xx, yy, pred_y, 50, cmap='RdBu',vmin=-1, vmax=1)
    ax_c = f.colorbar(contour)
    ax_c.set_label('$f(x, y)$')
    ax_c.set_ticks([-1, -0.75, -0.5, -0.25, 0, 0.25, 0.5, 0.75, 1])
    ax.scatter(X[:, 0], X[:, 1], c=y, s=50, linewidth=1,
                cmap="RdYlBu", edgecolor='white')

    XX, YY = np.meshgrid(np.linspace(round(min(min_xy)), round(max(max_xy)),X.shape[0]),np.linspace(round(min(min_xy)), round(max(max_xy)),X.shape[0]))
    positions = np.vstack([XX.ravel(), YY.ravel()])
    ax.contour(XX,YY,fz(positions.T).reshape(X.shape[0],X.shape[0]),[0], colors='black')

    ax.set(
       xlim=(min_xy[0]-border_xy[0], max_xy[0]+border_xy[0]),
       ylim=(min_xy[1]-border_xy[1], max_xy[1]+border_xy[1]),
       xlabel=xaxis, ylabel=yaxis)
    plt.title(title)
    plt.show()

def evaluate_grid(f, grid):
    z = np.zeros(len(grid))
    for i in range(len(grid)):
        z[i] = f(grid[i][0],grid[i][1])
    return z

def f1(x,y):
    return (x-10)**2 + (y-20)**2 - 400

def f2(x,y):
    return 0.5*(x+10)**2 + (y-20)**2 - 400

def f3(x,y):
    return 0.5*(x-10)**2 - (y+20)**2 - 400

def f4(x,y):
    return y - 20*x**2 - 5*x + 3

def f1_grid(grid):
    return evaluate_grid(f1, grid)

def f2_grid(grid):
    return evaluate_grid(f2, grid)

def f3_grid(grid):
    return evaluate_grid(f3, grid)

def f4_grid(grid):
    return evaluate_grid(f4, grid)

# Error de clasificación
def err_accuracy(x, y, f):
    acc = 0.0
    for i in range(y.size):
        if signo(f(x[i][0],x[i][1])) != y[i]:
            acc += 1
    return acc/y.size

plot_datos_cuad(x,y_ruido,f1_grid,'Clasificación realizada por f1 (circunferencia)')
print('Accuracy f1 (circunferencia):', err_accuracy(x,y,f1))

input("\n--- Pulsar tecla para continuar ---\n")

plot_datos_cuad(x,y_ruido,f2_grid,'Clasificación realizada por f2 (elipse)')
print('Accuracy f2 (elipse):', err_accuracy(x,y,f2))

input("\n--- Pulsar tecla para continuar ---\n")

plot_datos_cuad(x,y_ruido,f3_grid,'Clasificación realizada por f3 (hipérbola)')
print('Accuracy f3 (hipérbola):', err_accuracy(x,y,f3))

input("\n--- Pulsar tecla para continuar ---\n")

plot_datos_cuad(x,y_ruido,f4_grid,'Clasificación realizada por f4 (parábola)')
print('Accuracy f4 (parábola):', err_accuracy(x,y,f4))

input("\n--- Pulsar tecla para continuar ---\n")

###############################################################################
###############################################################################
###############################################################################

# EJERCICIO 2.1: ALGORITMO PERCEPTRON

# Algoritmo Perceptrón
def ajusta_PLA(datos, label, max_iter, vini):
    w = np.copy(vini)
    w.astype(np.float64)
    iterations = 0
    change = True
    while change and iterations < max_iter:
        change = False
        i = 0
        while i < label.size and iterations < max_iter:
            if signo(np.dot(w,datos[i])) != label[i]:
                w += label[i]*datos[i]
                change = True
            i += 1
            iterations += 1
    return w, iterations

max_iter = 100000
datos = np.concatenate((np.ones((x.shape[0],1),np.float64),x), axis=1)
label = y

print('PERCEPTRÓN (sobre datos sin ruido)')

# Null vector initialization
w, iterations = ajusta_PLA(datos, label, max_iter, np.zeros(3))

print('Iteraciones necesarias para converger (vector inicial nulo): {}'.format(iterations))

# Random initializations
iterations = []
for i in range(0,10):
    vini = np.random.uniform(0,1,3)
    w, it = ajusta_PLA(datos, label, max_iter, vini)
    iterations.append(it)
    print("vini:",vini,"\titerations:",it)

print('Valor medio de iteraciones necesario para converger (vectores iniciales aleatorios): {}'.format(np.mean(np.asarray(iterations))))

input("\n--- Pulsar tecla para continuar ---\n")

# Ahora con los datos del ejercicio 1.2.b
print('PERCEPTRÓN (sobre datos con ruido)')

max_iter = 100000
datos = np.concatenate((np.ones((x.shape[0],1),np.float64),x), axis=1)
label = y_ruido

# Null vector initialization
w, iterations = ajusta_PLA(datos, label, max_iter, np.zeros(3))

print('Iteraciones necesarias para converger (vector inicial nulo): {}'.format(iterations))

# Random initializations
iterations = []
for i in range(0,10):
    vini = np.random.uniform(0,1,3)
    w, it = ajusta_PLA(datos, label, max_iter, vini)
    iterations.append(it)
    print("vini:",vini,"\titerations:",it)

print('Valor medio de iteraciones necesario para converger (vectores iniciales aleatorios): {}'.format(np.mean(np.asarray(iterations))))

input("\n--- Pulsar tecla para continuar ---\n")

###############################################################################
###############################################################################
###############################################################################

# EJERCICIO 2.2: REGRESIÓN LOGÍSTICA CON STOCHASTIC GRADIENT DESCENT
print('REGRESIÓN LOGÍSTICA')

# Función que clasifica el item xi con la etiqueta 1 si P[yi=1/xi] >= 0.5
#    y con la etiqueta -1 en caso contrario
def clasificadorRL(w, xi):
    exp_wxi = np.exp(np.dot(w,xi))
    return signo(exp_wxi/(1+exp_wxi)-0.5)

# Algoritmo SGD adaptado a Regresión Logística
def sgdRL(x, y, w0, lr, error, batch_size, max_epochs):
    w = np.copy(w0)
    w.astype(np.float64)
    epoch = 0
    diff_w = error
    while diff_w >= error and epoch < max_epochs:
        epoch += 1
        w_prev = w.copy()
        minibatches = np.split(np.random.permutation(np.arange(y.size)),
                               range(batch_size,y.size,batch_size))
        for mb in minibatches:
            grad = np.zeros(w.size, np.float64)
            for i in mb:
                grad = grad + (y[i]/(1+np.exp(y[i]*np.dot(w,x[i]))))*x[i]
            w = w + (lr/mb.size)*grad
        diff_w = np.linalg.norm(w-w_prev)
    return w, epoch

# Error de entropía cruzada
def errRL(x, y, w):
    return np.mean(np.log(1+np.exp(-y*np.matmul(x,w))))

# Error de clasificación aplicado a Regresión Logística
def errAccuracyRL(x, y, w):
    acc = 0
    for i in range(y.size):
        if clasificadorRL(w,x[i]) != y[i]:
            acc += 1
    return acc/y.size

lr = 0.01
error = 0.01
batch_size = 1
max_epochs = 10000

v_epochs = []
v_E_in = []
v_E_test = []
v_E_acc_in = []
v_E_acc_test = []

print('(la ejecución de los 100 experimentos me tarda entre 1 y 2 minutos)')
for rep in range(100):
    # Genero muestra uniforme con tamaño N=100 sobre el cuadrado [0,2]x[0,2]
    #   etiquetada a partir de una recta con ecuación y = a*x + b
    a, b = simula_recta([0,2])
    x = simula_unif(100, 2, [0,2])
    x = np.concatenate((np.ones((x.shape[0],1),np.float64),x), axis=1)
    y = np.array([f(x[i][0],x[i][1],a,b) for i in range(x.shape[0])])

    # Aplico regresión logística
    w0 = np.zeros(x.shape[1])
    w, epochs = sgdRL(x, y, w0, lr, error, batch_size, max_epochs)
    v_epochs.append(epochs)

    # Calculo E_in y Acc_in
    v_E_in.append(errRL(x, y, w))
    v_E_acc_in.append(errAccuracyRL(x, y, w))

    # Genero muestra uniforme con tamaño N=1000 análoga a la anterior
    x = simula_unif(1000, 2, [0,2])
    x = np.concatenate((np.ones((x.shape[0],1),np.float64),x), axis=1)
    y = np.array([f(x[i][0],x[i][1],a,b) for i in range(x.shape[0])])

    # Calculo E_test y Acc_test
    v_E_test.append(errRL(x, y, w))
    v_E_acc_test.append(errAccuracyRL(x, y, w))

# Obtengo promedios
print('Valor medio de E_in: {}'.format(np.mean(np.asarray(v_E_in))))
print('Valor medio de E_test: {}'.format(np.mean(np.asarray(v_E_test))))
print('Valor medio de E_acc_in: {}'.format(np.mean(np.asarray(v_E_acc_in))))
print('Valor medio de E_acc_test: {}'.format(np.mean(np.asarray(v_E_acc_test))))
print('Valor medio de épocas necesarias para converger: {}'.format(np.mean(np.asarray(v_epochs))))

input("\n--- Pulsar tecla para continuar ---\n")

###############################################################################
###############################################################################
###############################################################################

#BONUS: Clasificación de Dígitos
print('BONUS')

# Funcion para leer los datos
def readData(file_x, file_y, digits, labels):
	# Leemos los ficheros
	datax = np.load(file_x)
	datay = np.load(file_y)
	y = []
	x = []
	# Solo guardamos los datos cuya clase sea la digits[0] o la digits[1]
	for i in range(0,datay.size):
		if datay[i] == digits[0] or datay[i] == digits[1]:
			if datay[i] == digits[0]:
				y.append(labels[0])
			else:
				y.append(labels[1])
			x.append(np.array([1, datax[i][0], datax[i][1]]))

	x = np.array(x, np.float64)
	y = np.array(y, np.float64)

	return x, y

# Lectura de los datos de entrenamiento
x, y = readData('datos/X_train.npy', 'datos/y_train.npy', [4,8], [-1,1])
# Lectura de los datos para el test
x_test, y_test = readData('datos/X_test.npy', 'datos/y_test.npy', [4,8], [-1,1])

#mostramos los datos
fig, ax = plt.subplots()
ax.scatter(np.squeeze(x[np.where(y == 1),1]), np.squeeze(x[np.where(y == 1),2]), c='blue', s=5, label='8')
ax.scatter(np.squeeze(x[np.where(y == -1),1]), np.squeeze(x[np.where(y == -1),2]), c='red', s=5, label='4')
ax.set(xlabel='Intensidad promedio', ylabel='Simetria', title='Digitos Manuscritos (TRAINING)')
ax.set_xlim((0, 1))
plt.legend()
plt.show()

input("\n--- Pulsar tecla para continuar ---\n")

fig, ax = plt.subplots()
ax.scatter(np.squeeze(x_test[np.where(y_test == 1),1]), np.squeeze(x_test[np.where(y_test == 1),2]), c='blue', s=5, label='8')
ax.scatter(np.squeeze(x_test[np.where(y_test == -1),1]), np.squeeze(x_test[np.where(y_test == -1),2]), c='red', s=5, label='4')
ax.set(xlabel='Intensidad promedio', ylabel='Simetria', title='Digitos Manuscritos (TEST)')
ax.set_xlim((0, 1))
plt.legend()
plt.show()

input("\n--- Pulsar tecla para continuar ---\n")

# Función de error para Regresión Lineal (error cuadrático medio)
def errLinReg(x,y,w):
    return np.mean((np.matmul(x,w)-y)**2)

# Función de error para Pocket (fracción de elementos mal clasificados)
def errPocket(x, y, w):
    err = 0
    for i in range(y.size):
        if signo(np.dot(w,x[i])) != y[i]:
            err += 1
    return err/y.size

#LINEAR REGRESSION FOR CLASSIFICATION

# Gradiente Descendente Estocástico para Regresión Lineal
def sgdLinReg(x, y, w0, lr, error, batch_size, max_epochs):
    w = np.copy(w0)
    w.astype(np.float64)
    epoch = 0
    while errLinReg(x,y,w) >= error and epoch < max_epochs:
        epoch += 1
        minibatches = np.split(np.random.permutation(np.arange(y.size)),
                               range(batch_size,y.size,batch_size))
        for mb in minibatches:
            w_aux = np.copy(w)
            for k in range(w.size):
                w[k] -= lr*(2/mb.size)*np.dot(np.matmul(x[mb,:],w_aux)-y[mb], x[mb,k])
    return w

w0 = np.zeros(x.shape[1])
lr = 0.01
error = 0.001
batch_size = 50
max_epochs = 1000

w_linreg = sgdLinReg(x, y, w0, lr, error, batch_size, max_epochs)
E_in_reglin = errLinReg(x,y,w_linreg)
E_test_reglin = errLinReg(x_test,y_test,w_linreg)

print('E_in para Regresión Lineal:', E_in_reglin)
print('E_acc_in para Regresión Lineal:', errPocket(x,y,w_linreg))
dibujar_nube_etiq_con_recta(x[:,1:], y, -w_linreg[1]/w_linreg[2], -w_linreg[0]/w_linreg[2], 8, 4, 0, 1, -7, 0,
                            'Solución de Regresión Lineal sobre la nube de puntos de entrenamiento')

input("\n--- Pulsar tecla para continuar ---\n")

print('E_test para Regresión Lineal:', E_test_reglin)
print('E_acc_test para Regresión Lineal:', errPocket(x_test,y_test,w_linreg))
dibujar_nube_etiq_con_recta(x_test[:,1:], y_test, -w_linreg[1]/w_linreg[2], -w_linreg[0]/w_linreg[2], 8, 4, 0, 1, -7, 0,
                            'Solución de Regresión Lineal sobre la nube de puntos de test')

input("\n--- Pulsar tecla para continuar ---\n")

#POCKET ALGORITHM

# Algoritmo Pocket
def pocket(x, y, w0, max_iter):
    w = np.copy(w0)
    w.astype(np.float64)
    w_best = np.copy(w)
    ein_best = errPocket(x,y,w_best)
    iterations = 0
    change = True
    while change and iterations < max_iter:
        change = False
        i = 0
        while i < y.size and iterations < max_iter:
            if signo(np.dot(w,x[i])) != y[i]:
                w += y[i]*x[i]
                change = True
                ein = errPocket(x,y,w)
                if ein < ein_best:
                    w_best = w
                    ein_best = ein
            i += 1
            iterations += 1
    return w_best

w0 = np.zeros(x.shape[1])
max_iter = 10000

w_pocket = pocket(x, y, w0, max_iter)
E_in_pocket = errPocket(x,y,w_pocket)
E_test_pocket = errPocket(x_test,y_test,w_pocket)

print('E_in para Pocket:', E_in_pocket)
dibujar_nube_etiq_con_recta(x[:,1:], y, -w_pocket[1]/w_pocket[2], -w_pocket[0]/w_pocket[2], 8, 4, 0, 1, -7, 0,
                            'Solución de Pocket sobre la nube de puntos de entrenamiento')

input("\n--- Pulsar tecla para continuar ---\n")

print('E_test para Pocket:', E_test_pocket)
dibujar_nube_etiq_con_recta(x_test[:,1:], y_test, -w_pocket[1]/w_pocket[2], -w_pocket[0]/w_pocket[2], 8, 4, 0, 1, -7, 0,
                            'Solución de Pocket sobre la nube de puntos de test')

input("\n--- Pulsar tecla para continuar ---\n")

# LINEAR REGRESSION + POCKET ALGORITHM

max_iter = 1000

w_lr_p = pocket(x, y, w_linreg, max_iter)
E_in_lr_p = errPocket(x,y,w_lr_p)
E_test_lr_p = errPocket(x_test,y_test,w_lr_p)

print('E_in para Reg. Lin. + Pocket:', E_in_lr_p)
dibujar_nube_etiq_con_recta(x[:,1:], y, -w_lr_p[1]/w_lr_p[2], -w_lr_p[0]/w_lr_p[2], 8, 4, 0, 1, -7, 0,
                            'Solución de Reg. Lin. + Pocket sobre la nube de puntos de entrenamiento')

input("\n--- Pulsar tecla para continuar ---\n")

print('E_test para Reg. Lin. + Pocket:', E_test_lr_p)
dibujar_nube_etiq_con_recta(x_test[:,1:], y_test, -w_lr_p[1]/w_lr_p[2], -w_lr_p[0]/w_lr_p[2], 8, 4, 0, 1, -7, 0,
                            'Solución de Reg. Lin. + Pocket sobre la nube de puntos de test')

input("\n--- Pulsar tecla para continuar ---\n")

#COTA SOBRE EL ERROR

d_vc = 3
delta = 0.05

epsilon_in = np.sqrt( (8/y.size)*np.log(4*((2*y.size)**d_vc+1)/delta) )
epsilon_test = np.sqrt( 1/(2*y_test.size)*np.log(2/delta) )

print('Cota de E_out para Regresión Lineal basada en E_in:\n\tE_out <=',E_in_reglin,'+',epsilon_in,'=',E_in_reglin+epsilon_in)
print('Cota de E_out para Regresión Lineal basada en E_test:\n\tE_out <=',E_test_reglin,'+',epsilon_test,'=',E_test_reglin+epsilon_test)
print()

print('Cota de E_out para Pocket basada en E_in:\n\tE_out <=',E_in_pocket,'+',epsilon_in,'=',E_in_pocket+epsilon_in)
print('Cota de E_out para Pocket basada en E_test:\n\tE_out <=',E_test_pocket,'+',epsilon_test,'=',E_test_pocket+epsilon_test)
print()

print('Cota de E_out para Reg. Lin. + Pocket basada en E_in:\n\tE_out <=',E_in_lr_p,'+',epsilon_in,'=',E_in_lr_p+epsilon_in)
print('Cota de E_out para Reg. Lin. + Pocket basada en E_test:\n\tE_out <=',E_test_lr_p,'+',epsilon_test,'=',E_test_lr_p+epsilon_test)
print()


# PDF says how many points exists in particular range

# Using CDF we shall visually see what % of versicolor flowers have petal_length of less than 2 ?
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt
import numpy as np

iris = pd.read_csv("iris.csv")

iris_setosa = iris.loc[iris["species"] == "setosa"];
iris_virginica = iris.loc[iris["species"] == "virginica"];
iris_versicolor = iris.loc[iris["species"] == "versicolor"];

counts, bin_edges = np.histogram(iris_setosa['petal_length'], bins=10,
                                 density = True)
pdf = counts/(sum(counts))
print(pdf);
print(bin_edges);
cdf = np.cumsum(pdf)
plt.plot(bin_edges[1:],pdf);
plt.plot(bin_edges[1:], cdf)

counts, bin_edges = np.histogram(iris_setosa['petal_length'], bins=20,
                                 density = True)
pdf = counts/(sum(counts))
plt.plot(bin_edges[1:],pdf);

plt.show();

# CDF --> cummulative sum of histogram
# CDF says, if Petal length x-axis value is 1.6 then
# roughly 82% of setosa flower exists
# CDF scale from 0 to 1, meaning % of the flower
# CDF can be used to compute accuracy in case of if else modelling

# Plots of CDF of petal_length for various types of flowers.

counts, bin_edges = np.histogram(iris_setosa['petal_length'], bins=10,
                                 density = True)
pdf = counts/(sum(counts))
print(pdf);
print(bin_edges)
cdf = np.cumsum(pdf)
plt.plot(bin_edges[1:],pdf)
plt.plot(bin_edges[1:], cdf)

# virginica
counts, bin_edges = np.histogram(iris_virginica['petal_length'], bins=10,
                                 density = True)
pdf = counts/(sum(counts))
print(pdf);
print(bin_edges)
cdf = np.cumsum(pdf)
plt.plot(bin_edges[1:],pdf)
plt.plot(bin_edges[1:], cdf)

#versicolor
counts, bin_edges = np.histogram(iris_versicolor['petal_length'], bins=10,
                                 density = True)
pdf = counts/(sum(counts))
print(pdf);
print(bin_edges)
cdf = np.cumsum(pdf)
plt.plot(bin_edges[1:],pdf)
plt.plot(bin_edges[1:], cdf)

plt.show();

# Misclassification error if you use petal_length only.


#!/usr/bin/env python

import numpy as np

def spiral(n):
    dx, dy = 0, -1
    x, y = n-1, n-1
    array = np.zeros([n, n], int)
    for i in range(1, n ** 2+1)[::-1]:
        array[x][y] = i
        nx, ny = x + dx, y + dy
        if 0 <= nx < n and 0 <= ny < n and array[nx][ny] == 0:
            x, y = nx, ny
        else:
            dx, dy = dy,-dx
            x, y = x + dx, y + dy
    return array

def printspiral(myarray):
    print
    n = range(len(myarray))
    for x in n:
        for y in n:
            print "%2i" % myarray[x][y],
        print

def test_spiral():
    assert printspiral(spiral(7)) == None

def isprime(n):
    if n < 2 or n % 2 == 0:
        return False

    for d in xrange(2, int(np.sqrt(n))+1):
        if n % d == 0:
            return False

    return True

def test_isprime():
    assert isprime(4) == False
    assert isprime(17) == True

def primeratio(array):
    counter = 0
    for d in np.diag(array):
        if isprime(d):
            counter += 1
    for d in np.diag(array[:, ::-1]):
        if isprime(d):
            counter += 1
    return counter * 1.0 / (2 * np.shape(array)[0] - 1)

def test_primeratio():
    assert abs(primeratio(spiral(7)) - 0.62) < 0.01

if __name__ == '__main__':
    n = 7
    while True:
        n += 2
        r = primeratio(spiral(n))
        print n, r
        if r < 0.1:
            print n
            break


#Hash functions and multiplanes

import numpy as np                # library for array and matrix manipulation
import pprint                     # utilities for console printing
from utils_nb import plot_vectors # helper function to plot vectors
import matplotlib.pyplot as plt   # visualization library

pp = pprint.PrettyPrinter(indent=4) # Instantiate a pretty printer
def basic_hash_table(value_l, n_buckets):

    def hash_function(value, n_buckets):
        return int(value) % n_buckets

    hash_table = {i:[] for i in range(n_buckets)} # Initialize all the buckets in the hash table as empty lists

    for value in value_l:
        hash_value = hash_function(value,n_buckets) # Get the hash key for the given value
        hash_table[hash_value].append(value) # Add the element to the corresponding bucket

    return hash_table

value_l = [100, 10, 14, 17, 97] # Set of values to hash
hash_table_example = basic_hash_table(value_l, n_buckets=10)
pp.pprint(hash_table_example)
P = np.array([[1, 1]]) # Define a single plane.
fig, ax1 = plt.subplots(figsize=(8, 8)) # Create a plot

plot_vectors([P], axes=[2, 2], ax=ax1) # Plot the plane P as a vector

# Plot  random points.
for i in range(0, 10):
        v1 = np.array(np.random.uniform(-2, 2, 2)) # Get a pair of random numbers between -4 and 4
        side_of_plane = np.sign(np.dot(P, v1.T))

        # Color the points depending on the sign of the result of np.dot(P, point.T)
        if side_of_plane == 1:
            ax1.plot([v1[0]], [v1[1]], 'bo') # Plot blue points
        else:
            ax1.plot([v1[0]], [v1[1]], 'ro') # Plot red points

plt.show()

P = np.array([[1, 2]])  # Define a single plane. You may change the direction

# Get a new plane perpendicular to P. We use a rotation matrix
PT = np.dot([[0, 1], [-1, 0]], P.T).T

fig, ax1 = plt.subplots(figsize=(8, 8)) # Create a plot with custom size

plot_vectors([P], colors=['b'], axes=[2, 2], ax=ax1) # Plot the plane P as a vector

# Plot the plane P as a 2 vectors.
# We scale by 2 just to get the arrows outside the current box
plot_vectors([PT * 4, PT * -4], colors=['k', 'k'], axes=[4, 4], ax=ax1)

# Plot 20 random points.
for i in range(0, 20):
        v1 = np.array(np.random.uniform(-4, 4, 2)) # Get a pair of random numbers between -4 and 4
        side_of_plane = np.sign(np.dot(P, v1.T)) # Get the sign of the dot product with P
        # Color the points depending on the sign of the result of np.dot(P, point.T)
        if side_of_plane == 1:
            ax1.plot([v1[0]], [v1[1]], 'bo') # Plot a blue point
        else:
            ax1.plot([v1[0]], [v1[1]], 'ro') # Plot a red point

plt.show()

P = np.array([[1, 1]])      # Single plane
v1 = np.array([[1, 2]])     # Sample point 1
v2 = np.array([[-1, 1]])    # Sample point 2
v3 = np.array([[-2, -1]])   # Sample point 3

np.dot(P, v1.T)
np.dot(P, v2.T)
np.dot(P, v3.T)

def side_of_plane(P, v):
    dotproduct = np.dot(P, v.T) # Get the dot product P * v'
    sign_of_dot_product = np.sign(dotproduct) # The sign of the elements of the dotproduct matrix
    sign_of_dot_product_scalar = sign_of_dot_product.item() # The value of the first item
    return sign_of_dot_product_scalar

side_of_plane(P, v1) # In which side is [1, 2]
side_of_plane(P, v2) # In which side is [-1, 1]
side_of_plane(P, v3) # In which side is [-2, -1]

P1 = np.array([[1, 1]])   # First plane 2D
P2 = np.array([[-1, 1]])  # Second plane 2D
P3 = np.array([[-1, -1]]) # Third plane 2D
P_l = [P1, P2, P3]  # List of arrays. It is the multi plane

# Vector to search
v = np.array([[2, 2]])

def hash_multi_plane(P_l, v):
    hash_value = 0
    for i, P in enumerate(P_l):
        sign = side_of_plane(P,v)
        hash_i = 1 if sign >=0 else 0
        hash_value += 2**i * hash_i
    return hash_value

np.random.seed(0)
num_dimensions = 2 # is 300 in assignment
num_planes = 3 # is 10 in assignment
random_planes_matrix = np.random.normal(
                       size=(num_planes,
                             num_dimensions))
print(random_planes_matrix)

v = np.array([[2, 2]])

# Side of the plane function. The result is a matrix
def side_of_plane_matrix(P, v):
    dotproduct = np.dot(P, v.T)
    sign_of_dot_product = np.sign(dotproduct) # Get a boolean value telling if the value in the cell is positive or negative
    return sign_of_dot_product

sides_l = side_of_plane_matrix(
            random_planes_matrix, v)
sides_l

def hash_multi_plane_matrix(P, v, num_planes):
    sides_matrix = side_of_plane_matrix(P, v) # Get the side of planes for P and v
    hash_value = 0
    for i in range(num_planes):
        sign = sides_matrix[i].item() # Get the value inside the matrix cell
        hash_i = 1 if sign >=0 else 0
        hash_value += 2**i * hash_i # sum 2^i * hash_i

    return hash_value

hash_multi_plane_matrix(random_planes_matrix, v, num_planes)

word_embedding = {"I": np.array([1,0,1]),
                   "love": np.array([-1,0,1]),
                   "learning": np.array([1,0,1])
                  }
words_in_document = ['I', 'love', 'learning', 'not_a_word']
document_embedding = np.array([0,0,0])
for word in words_in_document:
    document_embedding += word_embedding.get(word,0)

print(document_embedding)


## Sum of an array
print("==========Answer 1 Sum of an array using recursive========")
#
#This function will accept an array numbers. this function will return sum of array.
# the function will call itself until the array length is zero
def getSum(a):

    if len(a)==0:
        return 0
    else:
        return a[0] + getSum(a[1:])

num=[100,25,26,78 ,34, 45, 27, 57]

SumofArray=getSum(num)
print('           ')

print("Recursive function SumofArray",num, "is",SumofArray)
print('           ')
print("***********************************************************")

print("==========Answer 1 Sum of an array using function ========")

#The reduce() function accepts a function and a sequence and returns a single value calculated as follows:
from functools import reduce
num=[2,34,67,89,34,56,78]

def sum(a,b):
    return a+b
# first method using function

sumarray= reduce(sum,num)
# Second method using lambda
sumarray1= reduce(lambda a,b:a+b ,num)
print('           ')

print("using Lambda value of sum",num,"is",sumarray)
print('           ')
print("***********************************************************")
print("Using function value of sum",num,"is",sumarray1)
print('           ')
print("==========Answer2 Product of an array========")
#This function will accept an array numbers. this function will return Product of an array.
# The function will call itself until the array length is zero
import numpy as np

def mul(a):
    if len(a)==0:
        return 1
    else:
        return  a[0] * mul(np.array(a[1:]))

num=[10,5,25,2,100,200]

MulofArray=mul(num)
print('           ')

print("Recursive function MulofArray value",num, "is ",MulofArray)

print('           ')
print("***********************************************************")
print('           ')
print("==========Answer2 Product of an array using function========")
def mul(a,b):
    return a*b
# first method using function

mularray= reduce(mul,num)
# Second method using lambda
mularray1= reduce(lambda a,b:a*b ,num)
print('           ')
print("using function value of MulofArray value",num, "is" ,mularray)
print('           ')
print('=================================')
print('           ')
print("using lambda value of MulofArray value",num, "is",mularray1)
print('           ')
print('           ')
print("==========Answer3 Remove all odd numbers========")
#This function will accept an array numbers. this function will return an array after elemeneting the odd numbers.
# The function will call itself until the array length is zero,When the lenth of array is not 0 then the first element of the array
#will be inspected is even or not.Odd numbers
def getodd(a):
    if len(a)==0:
        return[]
    elif  a[0]%2==1: #cheching if the number is odd
        return  [a[0]]+ getodd(a[1:])
    else :
        return getodd(a[1:])

num=[10,1,25,2,101,200]

getoddofArray=getodd(num)
print('           ')
print("Recursive function Get_Odd No Of Array",num, "is",getoddofArray)
print('           ')
print("==========Answer3 Remove all odd numbers using function========")

def oddnum(n):
    return n%2!=0

num=[18,22,34,43,59,6,7,85,97,10,11,12,89,39,14]
# in python we have filter function that will take the value from filter
# first method using lambda
odd=list(filter(lambda n:n%2!=0 ,num))
# Second method using function
odd1 =list(filter(oddnum,num))
print('           ')
print("using lambda returns odd numbers value",num, "are",odd)
print('           ')
print('=================================')
print('           ')
print("using function returns odd numbers value",num,  "are",odd1)
print('           ')
print('           ')
print("==========Answer4 Remove all even numbers========")
#This function will accept an array numbers. this function will return Remove all even numbers.
# The function will call itself until the array length is zero
def geteven(a):
    if len(a)==0:
        return[]
    elif  a[0]%2==0:#cheching if the number is even
        #print(a[1:])
        #print("A is ",a[0] )
        #print("value is ",a[0] * getmul(a[1:]))

        return  [a[0]]+ geteven(a[1:])
    else : #a[0]%2==1:
        return geteven(a[1:])

num=[10,1,25,2,101,200]

getevenofArray=geteven(num)
print('           ')
print("Recursive function Get_even No Of Array",num, "is",getevenofArray)
print('           ')
print("=====================================")
print('           ')
print("==========Answer4 Remove all even numbers using function========")

def evennum(n):
    return n%2==0

num=[18,22,34,43,59,6,7,85,97,10,11,12,89,39,14]
# in python we have filter function that will take the value from filter
# first method using lambda
even=list(filter(lambda n:n%2==0 ,num))
# Second method using function
even1 =list(filter(evennum,num))
print('           ')
print("using lambda returns even numbers value",num ,"are",even)
print('=================================')
print('           ')
print("using function returns even numbers value ",num ,"are",even1)
print('           ')

print("==========Answer5 Replace a given character with '*'========")
print('           ')
#This function will accept an array numbers. this function will return Replace a given character with '*'.
# The function will call itself until the array length is zero
name="adarshasachan"
def remove(r, s):
    if not s:
        return ''
    if r == s[0]:# inspecting the first charactor
        return "*"+ remove(r, s[1:])#replacing with *
    else:
        return s[0] + remove(r, s[1:])#not replacing the first charactor
# examle from name Replaceing a  character with '*'
string=remove('a',name)
print('           ')
print("Recursive function Replacing A  character to * from",name,"is" ,string)

print("=====================================")

print('           ')
print("==========Answer6 Find index in array for item.========")

#This function will accept2 parameter an array and counter. this function will return Find index in array for item..
# The function will call itself until not length is zero
def iarray(arr, cha):
  global counter , inputlen
  counter= counter + 1
  if not arr:
    return -1
  if arr[0] == cha:#checking the frist element of array is same as the string that to be found.
    arr.index(cha)
    return(counter-1)
  elif inputlen == counter-1:#if seeach element is not present the return -1
    return -1
  return iarray(arr[1:], cha)
Input1 = ['hjhj','lpls','tali']
Input2 = 'tali'
counter = 0
inputlen = 0
inputlen = len(Input1)
print('           ')
print("======================================================")
print("   String value :" +  str(Input1))
print("    string value :" +  str(Input2))
print(" Recursive function Find index in array for item in a given String :" + str(iarray(Input1, Input2)))

print("=====================================")

print('           ')
print("==========Answer7 Sum of Digits ========")
#This function will accept an  array. this function will return Sum of Digits.
# The function will call itself until the array length is zero
def sum_digit(n):
    if n > 0:# checking if number is greater than 0
        return sum_digit(n // 10) + n % 10 #
    else:
        return 0
number=14567
sumnumber=sum_digit(number)
print('           ')
print("Recursive function Sum of digit",number,"is",sumnumber)

print("=====================================")
print('           ')
print("==========Answer8 Print an Array. ========")
#This function will accept an  array. this function will return Print an array.
# The function will call itself until the the array length is 1
def Printarray(arr):
     if len(arr)== 1:
        return print(arr[0])
     else:
        print(arr[0])
        return Printarray(arr[1:])
num = [1,2,13,8,5,6]
print('           ')
print("Recursive function Print an Array",num,"is")
Printarray(num)

print("=====================================")
print('           ')
print('           ')
print("==========Answer9 Find the minimum element in an array of integers. ========")
#This function will accept an  array. this function will return inimum element in an array of integers.

def Minelement(a):
    if len(a)==2:

        if a[0]<a[1]:# comparing between two number
            return a[0]
        else:
            return a[1]
    else:
        #print("value of a",a[0])
        #print("value of min ",Minelement(a[1:]))
        if a[0]<Minelement(a[1:]):
            return a[0]
        else:
            return Minelement(a[1:])

num=[15,7,9,4,6,7]
i=Minelement(num)
print('           ')
print(" Recursive function minimum element in an array of integer",num,"is",i)

print("=====================================")
print('           ')
print("==========Answer10 Verify the parentheses ========")
#This function will accept an  array. this function will Verify the parentheses.
# The function will call itself until the array length is zero
def index (counter,a):
    if len(a)==0:
        if counter==0:
            return True
        else:
            return False
    else:
        len (a)< 0
        if a[0] =="(":#if open ( found then increasing the counter
            counter=counter +1
        elif a[0]==")":#if close ) found then decreasing the counter
            counter=counter -1
        return index(counter,a[1:])
counter=0
num="((aaaa(sss()))"
#index(counter,num)
print('           ')
print('           ')
print("Recursive function to Verify the parentheses from :",num,"is .."+ str(index(counter,num)))
print("***************************")
print('           ')
print('           ')
print("END")


from sympy import *

def sympy_derivative():
    # 定义表达式的变量名称
    x1, x2 = symbols('x1 x2')
    # 定义表达式内容
    Y = x1 ** 2 + x2 ** 2
    # 计算 x2对应的偏导数
    return diff(Y, x2)

# sympy 带入值计算
func = sympy_derivative()
print(func)  # 输出结果2*x2
print(func.evalf(subs={'x2': 6}))  # 把x2 等于6 带入计算 结果 为12

# 其中 func.evalf(subs={}) 为特征值带入计算的api，需特殊说明的是，该函数运行结果，可能还只是表达式，其主要是求解已经给的特征值


import unittest
import numpy as np
import time as tm
import matplotlib.pyplot as plt
from nptest import nptest
import operator

class ArrayCreationTests(unittest.TestCase):
    def test_PrintVersionString(self):
        print(np.__version__)

    def test_simpleShape_1(self):

        a=np.array([1,2,3])
        x=a.shape[0]

        print(x)

    def test_Slice3x2x2_1(self):

        x = np.arange(12).reshape(3, 2, 2);
        y = x[1:];
        y *= 99;
        print(x)

    def test_asfarray_1(self):

        a = np.asfarray([2, 3])
        print(a)

        b = np.asfarray([2, 3], dtype='float')
        print(b)

        c = np.asfarray([2, 3], dtype='int8')
        print(c)

    def test_asmatrix_1(self):

        x = np.array([[1, 2], [3, 4]])
        m = np.asmatrix(x)

        x[0,0] = 5

        print(m)

    def test_copy_1(self):
        x = np.array([1, 2, 3])
        y = x
        z = np.copy(x)
        # Note that, when we modify x, y changes, but not z:

        x[0] = 10
        print(x[0] == y[0])
        #True
        print(x[0] == z[0])
        #False

    def test_linspace_1(self):
        a = np.linspace(2.0, 3.0, num=5)
        print(a)

        b = np.linspace(2.0, 3.0, num=5, endpoint=False)
        print(b)

        c = np.linspace(2.0, 3.0, num=5, retstep=True)
        print(c)

    def test_linspace_2(self):
        a = np.linspace(2+1.3j, 3+5.6j, num=5, dtype=np.complex)
        print(a)

        b = np.linspace(2.0, 3.0, num=5, endpoint=False)
        print(b)

        c = np.linspace(2.0, 3.0, num=5, retstep=True)
        print(c)

    def test_linspace_3(self):
        a = np.linspace(2.0, 3.0, num=5, dtype= np.longlong)
        print(a)

        b = np.linspace(2.0, 3.0, num=5, endpoint=False, dtype= np.longlong)
        print(b)

        c = np.linspace(2.0, 3.0, num=5, retstep=True, dtype= np.longlong)
        print(c)

    def test_logspace_1(self):
        a = np.logspace(2.0, 3.0, num=4)
        print(a)

        b = np.logspace(2.0, 3.0, num=4, endpoint=False)
        print(b)

        c = np.logspace(2.0, 3.0, num=4, base=2.0)
        print(c)

    def test_logspace_2(self):
        a = np.logspace(2, 3, num=4, dtype=np.longlong)
        print(a)

        b = np.logspace(2.0, 3.0, num=4, endpoint=False, dtype=np.longlong)
        print(b)

        c = np.logspace(2.0, 3.0, num=4, base=2.0, dtype=np.longlong)
        print(c)

    def test_geomspace_1(self):
        a = np.geomspace(1, 1000, num=4)
        print(a)

        b = np.geomspace(1, 1000, num=3, endpoint=False)
        print(b)

        c = np.geomspace(1, 1000, num=4, endpoint=False)
        print(c)

        d = np.geomspace(1, 256, num=9)
        print(d)

    def test_meshgrid_1(self):
        nx = 3
        ny = 2

        x = np.linspace(0, 1, nx)
        y = np.linspace(0, 1, ny)

        xv = np.meshgrid(x)
        print(xv)

        print("************")

        xv, yv = np.meshgrid(x, y)
        print(xv)
        print(yv)

        print("************")

        xv, yv = np.meshgrid(x, y, sparse=True)
        print(xv)
        print(yv)

        print("************")

        x = np.arange(-5, 5, 1)
        y = np.arange(-5, 5, 1)
        xx, yy = np.meshgrid(x, y, sparse=True)
        print(xx)
        print(yy)

        print("************")

    def test_meshgrid_1_longlong(self):
        nx = 3
        ny = 2

        x = np.linspace(0, 100, nx, dtype=np.longlong)
        y = np.linspace(0, 100, ny, dtype=np.longlong)

        xv = np.meshgrid(x)
        print(xv)

        print("************")

        xv, yv = np.meshgrid(x, y)
        print(xv)
        print(yv)

        print("************")

        xv, yv = np.meshgrid(x, y, sparse=True)
        print(xv)
        print(yv)

        print("************")

        x = np.arange(-5, 5, 1, dtype=np.longlong)
        y = np.arange(-5, 5, 1, dtype=np.longlong)
        xx, yy = np.meshgrid(x, y, sparse=True)
        print(xx)
        print(yy)

        print("************")

    def test_meshgrid_2(self):
        nx = 3
        ny = 2
        nz = 2

        x = np.linspace(0, 1, nx)
        y = np.linspace(4, 5, ny)
        z = np.linspace(8, 9, nz)
        xv, yv, zv = nptest.meshgrid(x, y, z, indexing='ij')
        print(xv)
        print(yv)
        print(zv)

        print("************")

        xv, yv, zv = np.meshgrid(x, y,z, sparse=True)
        print(xv)
        print(yv)
        print(zv)

        print("************")

        x = np.arange(-2, 2, 1)
        y = np.arange(-2, 2, 1)
        z = np.arange(2, -2, -1)

        xx, yy, zz = np.meshgrid(x, y, z, copy=True)
        print(xx)
        print(yy)
        print(zz)

        print("************")

    def test_OneDimensionalArray(self):
        l = [12.23, 13.32, 100, 36.32]
        print("Original List:",l)
        a = np.array(l)
        print("One-dimensional numpy array: ",a)
        print(a.shape)
        print(a.strides)

    def test_arange_2to11(self):
        a =  np.arange(2, 11, 1, dtype = np.int8)
        print(a)

        print(a.shape)
        print(a.strides)

    def test_arange_2to11_double(self):
        a =  np.arange(2.5, 11.5, 2)
        print(a)

        print(a.shape)
        print(a.strides)

    def test_arange_2to11_float(self):
        a =  np.arange(2.5, 37.7, 2.2, dtype=np.float32)
        print(a)

        print(a.shape)
        print(a.strides)

    def test_arange_reshape_33(self):
        a =  np.arange(2, 11).reshape(3,3)
        print(a)

        print(a.shape)
        print(a.strides)

    def test_arange_reshape_53(self):
        a =  np.arange(0, 15).reshape(5,3)
        print(a)

        print(a.shape)
        print(a.strides)

    def test_reverse_array(self):
      x = np.arange(0,40)
      print("Original array:")
      print(x)
      print("Reverse array:")
      x = x[::-1]
      print(x)

      y = x + 100
      print(y)

      z = x.reshape(5,-1)
      print(z)

    def test_1_OnBorder_0Inside(self):
      x = np.ones((15,15), dtype= np.double)
      print("Original array:")
      print(x)
      print(x.shape)
      print(x.strides)
      print("1 on the border and 0 inside in the array")
      x[1:-1,1:-1] = 0
      print(x)
      print(x.shape)
      print(x.strides)

    def test_1_OnBorder_0Inside_2(self):
      x = np.arange(0,225, dtype= np.double).reshape(15,15)
      print("Original array:")
      print(x)
      print(x.shape)
      print(x.strides)
      print("1 on the border and 0 inside in the array")
      x = x[1:-1,1:-1];
      print(x)
      print(x.shape)
      print(x.strides)

    def test_checkerboard_1(self):
        x = np.ones((3,3))
        print("Checkerboard pattern:")
        x = np.zeros((8,8),dtype=int)
        x[1::2,::2] = 1
        x[::2,1::2] = 1
        print(x)

    def test_F2C_1(self):
       fvalues = [0, 12, 45.21, 34, 99.91]
       F = np.array(fvalues, dtype=np.float32)
       print("Values in Fahrenheit degrees:")
       print(F)
       print("Values in  Centigrade degrees:")

       C = 5*F/9 - 5*32/9;
       print(C)

    def test_RealImage_float_1(self):
        x = np.array([1.0,1.5,1.74,1.9])
        y = np.array([0.1,0.3,0.5,0.7])
        print("Original array:x ",x)
        print("Original array:y ",y)
        print("Real part of the array:")
        print(x.real)
        print(y.real)
        print("Imaginary part of the array:")
        print(x.imag)
        print(y.imag)

    def test_ArrayStats_1(self):
        x = np.array([1,2,3], dtype=np.float64)
        print("Size of the array: ", x.size)
        print("Length of one array element in bytes: ", x.itemsize)
        print("Total bytes consumed by the elements of the array: ", x.nbytes)

    def test_ndarray_flatten(self):
      x = np.arange(0.73,25.73, dtype= np.double).reshape(5,5)
      y = x.flatten()
      print(x)
      print(y)

      y = x.flatten(order='F')
      print(y)

      y = x.flatten(order='K')
      print(y)

    def test_ndarray_flatten_longlong(self):
      x = np.arange(7,32, dtype= np.longlong).reshape(5,5)
      y = x.flatten()
      print(x)
      print(y)

      y = x.flatten(order='F')
      print(y)

      y = x.flatten(order='K')
      print(y)

    def test_ndarray_byteswap(self):
      x = np.arange(32,64, dtype= np.int16)
      print(x)
      y = x.byteswap(True)
      print(y)

      x = np.arange(32,64, dtype= np.int32)
      print(x)
      y = x.byteswap(True)
      print(y)

      x = np.arange(32,64, dtype= np.int64)
      print(x)
      y = x.byteswap(True)
      print(y)

    def test_ndarray_view(self):
      x = np.arange(256+32,256+64, dtype= np.int16)
      print(x)
      print(x.shape)
      print(x.dtype)

      y = x.view(np.uint8)
      print(y)
      print(y.shape)
      print(y.dtype)

      print("modifying data")
      y[1] = 99
      print(x)

    def test_ndarray_view_1(self):
      x = np.arange(0,32, dtype= np.int16).reshape(2,-1,4)
      print("X")
      print(x)
      print(x.shape)

      y = x.T

      print("Y")
      print(y)
      print(y.shape)

      z = y.view()
      z[0] = 99
      print("Z")
      print(z)
      print(z.shape)

      print("X")
      print(x)
      print("Y")
      print(y)

    def test_ndarray_view2(self):
      x = np.arange(256+32,256+64, dtype= np.int16)
      print(x)
      print(x.shape)
      print(x.dtype)

      y = x.view(np.uint32)
      print(y)
      print(y.shape)
      print(y.dtype)

      print("modifying data")
      y[1] = 99
      y[5] = 88
      print(y)
      print(x)

    def test_ndarray_view2_reshape(self):
      x = np.arange(65470+32,65470+64, dtype= np.uint16).reshape(2,2,-1)
      print(x)
      print(x.shape)
      print(x.dtype)

      z = x[:,:,[2]]
      print(z)

      y = z.view().reshape(-1);
      print(y)
      print(y.shape)
      print(y.dtype)

    def test_ndarray_view3(self):
      x = np.arange(256+32,256+64, dtype= np.int16)
      print(x)
      print(x.shape)
      print(x.dtype)

      y = x.view(np.uint64)
      print(y)
      print(y.shape)
      print(y.dtype)

      print("modifying data")
      y[1] = 99
      y[5] = 88
      print(y)
      print(x)

    def test_ndarray_delete1(self):
      x = np.arange(0,32, dtype= np.int16).reshape(8,4)
      print("X")
      print(x)
      print(x.shape)

      y = np.delete(x, 0, axis=1)
      y[1] = 99
      print("Y")
      print(y)
      print(y.shape)

      print("X")
      print(x)

    def test_ndarray_delete2(self):
      x = np.arange(0,32, dtype= np.int16)
      print("X")
      print(x)
      print(x.shape)

      y = np.delete(x, 1,0)

      print("Y")
      print(y)
      print(y.shape)

      print("X")
      print(x)

    def test_ndarray_delete3(self):
      x = np.arange(0,32, dtype= np.int16).reshape(8,4)
      print("X")
      print(x)
      print(x.shape)

      mask = np.ones_like(x, dtype=np.bool)
      mask[:,[0]] = False
      print(mask)
      y = x[mask].reshape(8,3)

      print("Y")
      print(y)
      print(y.shape)

      print("X")
      print(x)

    def test_ndarray_unique_1(self):

      x = np.array([1,2,3,1,3,4,5,4,4]);

      print("X")
      print(x)

      uvalues, indexes, inverse, counts = np.unique(x, return_counts = True, return_index=True, return_inverse=True);

      print("uvalues")
      print(uvalues)
      print("indexes")
      print(indexes)
      print("inverse")
      print(inverse)
      print("counts")
      print(counts)

    def test_ndarray_unique_2(self):

      x = np.array([1,2,3,1,98,97,96,94,3,4,5,4,4,1,9,6,9,11,23,9,5,0,11,12]).reshape(6,4);

      print("X")
      print(x)
      uvalues, indexes, inverse, counts = np.unique(x, return_counts = True, return_index=True, return_inverse=True, axis=0);

      print("uvalues")
      print(uvalues)
      print("indexes")
      print(indexes)
      print("inverse")
      print(inverse)
      print("counts")
      print(counts)

      uvalues, indexes, inverse, counts = np.unique(x, return_counts = True, return_index=True, return_inverse=True, axis=1);

      print("uvalues")
      print(uvalues)
      print("indexes")
      print(indexes)
      print("inverse")
      print(inverse)
      print("counts")
      print(counts)

    def test_ndarray_where_1(self):
      x = np.array([1,2,3,1,3,4,5,4,4]).reshape(3,3)
      print("X")
      print(x)
      y = np.where(x == 3)
      print("Y")
      print(y)

    def test_ndarray_where_2(self):
      x = np.array([1,2,3,1,3,4,5,4,4], dtype=np.int32).reshape(3,3)
      print("X")
      print(x)
      y = np.where(x == 3)
      print("Y")
      print(y)

      z = x[y]
      print("Z")
      print(z)

    def test_ndarray_where_3(self):
      x = np.arange(0, 1000, dtype=np.int32).reshape(-1,10)
      #print("X")
      #print(x)
      y = np.where(x % 10 == 0)
      #print("Y")
      #print(y)

      z = x[y]
      print("Z")
      print(z)

    def test_ndarray_where_4(self):
      x = np.arange(0, 3000000, dtype=np.int32)
      #print("X")
      #print(x)
      y = np.where(x % 7 == 0)
      print("Y")
      print(y)

      z = x[y]
      m = np.mean(z);
      print("M")
      print(m)

    def test_ndarray_where_5(self):
        a = np.arange(10)

        b = np.where(a < 5, a, 10*a)
        print(b)

        a = np.array([[0, 1, 2], [0, 2, 4], [0, 3, 6]])
        b = np.where(a < 4, a, -1)  # -1 is broadcast
        print(b)

        c = np.where([[True, False], [True, True]],
                     [[1, 2], [3, 4]],
                     [[9, 8], [7, 6]])
        print(c)

    def test_ndarray_unpackbits_1(self):
      x = np.arange(0,12, dtype=np.uint8).reshape(3,-1)
      print("X")
      print(x)
      y = np.unpackbits(x, 1);
      print("Y")
      print(y)

      z = np.packbits(y, 1)
      print("Z")
      print(z)

    def test_arange_slice_1(self):
        a =  np.arange(0, 1024, dtype=np.int16).reshape(2,4, -1)
        print("A")
        #print(a)
        print(a.shape)
        print(a.strides)

        b = a[:,:,122]
        print("B")
        print(b)
        print(b.shape)
        print(b.strides)

        c = a[:,:,[122]]
        print("C")
        print(c)
        print(c.shape)
        print(c.strides)

        c2 = np.arange(0,8, dtype=np.int16).reshape(2,4,1);
        print("C2")
        print(c2)
        print(c2.shape)
        print(c2.strides)

        d = a[:,:,[122,123]]
        print("D")
        print(d)
        print(d.shape)
        print(d.strides)

    def test_arange_slice_2(self):
        a =  np.arange(0, 32, dtype=np.int16).reshape(2,4, -1)
        print("A")
        #print(a)
        print(a.shape)
        print(a.strides)

        # b has unexpected strides.  If a copy from A is made first
        b = a[:,:,[2]]
        print("B")
        print(b)
        print(b.shape)
        print(b.strides)

    def test_arange_slice_2A(self):
        a =  np.arange(0, 32, dtype=np.int16).reshape(2,4, -1)
        print("A")
        #print(a)
        print(a.shape)
        print(a.strides)

        # b has unexpected strides.  If a copy from A is made first
        b = a[:,:, np.where(a > 20)]
        print("B")
        print(b)
        print(b.shape)
        print(b.strides)

    def test_arange_slice_2B(self):
        a =  np.arange(0, 32, dtype=np.int16).reshape(2,4, -1)
        b =  np.arange(100, 132, dtype=np.int16).reshape(2,4, -1)
        print("A")
        #print(a)
        print(a.shape)
        print(a.strides)

        # b has unexpected strides.  If a copy from A is made first
        b[:,:,[2]] = a[:,:,[2]]
        print("B")
        print(b)
        print(b.shape)
        print(b.strides)

    def test_arange_slice_2C(self):
        a =  np.arange(0, 32, dtype=np.int16).reshape(2,4, -1)
        b =  np.arange(100, 132, dtype=np.int16).reshape(2,4, -1)
        print("A")
        #print(a)
        print(a.shape)
        print(a.strides)

        b[:,:,[2]] |= a[:,:,[2]]
        print("B")
        print(b)
        print(b.shape)
        print(b.strides)

    def test_arange_slice_2C2(self):
        a =  np.arange(0, 32, dtype=np.int16).reshape(2,4, -1)
        b =  np.arange(100, 132, dtype=np.int16).reshape(2,4, -1)
        print("A")
        #print(a)
        print(a.shape)
        print(a.strides)

        # b has unexpected strides.  If a copy from A is made first
        aarray = a[:, :, [2]]
        barray = b[:, :, [2]]
        carray = barray | aarray
        print("B")
        print(carray)
        print(carray.shape)
        print(carray.strides)

    def test_ndarray_NAN(self):

        _max = 5
        output = np.ndarray(shape=(_max,), dtype = np.float);
        output[:] = np.NaN;

        print(output)
        print(output.shape)

    def test_insert_1(self):

        a = np.array([[1, 1], [2, 2], [3, 3]])
        b = np.insert(a, 1, 5);

        c = np.insert(a, 0, [999,100,101])

        print(a)
        print(a.shape)

        print("B")
        print(b)
        print(b.shape)
        print(b.strides)

        print("C")
        print(c)
        print(c.shape)
        print(c.strides)

    def test_insert_2(self):

        #print(np.source(np.insert))

        a = np.array([1, 1, 2, 2, 3, 3])
        b = np.array([90, 91, 92, 92, 93, 93])
        c = np.insert(a, slice(None), b);
        #d = nptest.insert(a, slice(None), b);

        print(a)
        print(a.shape)

        print("B")
        print(b)
        print(b.shape)
        print(b.strides)

        print("C")
        print(c)
        print(c.shape)
        print(c.strides)

        #print(d)
        #print(d.shape)

    def test_append_1(self):

        a = np.array([[1, 1], [2, 2], [3, 3]])
        b = np.append(a, 1);

        print(a)
        print(a.shape)

        print(b)
        print(b.shape)
        print(b.strides)

    def test_append_2(self):

        a = np.array([[1, 1], [2, 2], [3, 3]])
        b = np.append(a, [4,4]);

        print(a)
        print(a.shape)

        print(b)
        print(b.shape)
        print(b.strides)

    def test_append_3(self):

        a = np.array([[1, 1], [2, 2], [3, 3]])
        b = np.array([[4, 4], [5, 5], [6, 6]])
        c = np.append(a, b);

        print(a)
        print(a.shape)

        print(b)
        print(b.shape)

        print(c)
        print(c.shape)
        print(c.strides)

    def test_append_4(self):

        a = np.array([1, 1, 2, 2, 3, 3]).reshape(2,-1)
        b = np.array([4, 4, 5, 5, 6, 6]).reshape(2,-1)
        c = np.append(a, b, axis=1)

        print(a)
        print(a.shape)
        print("")

        print(b)
        print(b.shape)
        print("")

        print(c)
        print(c.shape)
        print(c.strides)
        print("")

    def test_append_msever_1(self):

        arr = np.array([[1,2,3],[4,5,6]])
        row = np.array([7,8,9])
        arr = np.append(arr,[row],axis= 0)
        print(arr)

    def test_tuple_msever_2(self):

        a = np.array((1,2,3))
        print(a)
        b = np.array((2,3,4))
        print(b)

        c = np.column_stack((a,b))
        print(c)

    def test_slice_msever_1(self):

        a=np.array([[1,3,0],[0,0,5]])

        col1 = a[:,0]
        col2 = a[:,1]
        col3 = a[:,2]

        print(col1)
        print(col2)
        print(col3)

    def test_hsplit_msever_1(self):

        a=np.array([[1,3,0],[0,0,5]])

        row, col = np.hsplit(np.argwhere(a),2)

        print(row)
        print(col)

    def test_take_msever_1(self):

        testVector = np.array([ 1.011163, 1.01644999999999, 1.01220500000001, 1.01843699999999, 1.00985100000001, 1.018964, 1.005825, 1.016707, 8.11556899999999, 1.010744, 1.01700600000001, 1.01323099999999, 1.010389, 1.015216, 1.015418, 1.01704600000001, 1.01191, 1.01164299999999, 1.01062400000001, 1.014199, 1.012952, 1.017645, 1.01591999999999, 1.018655, 1.00942400000001, 1.012852, 1.010543, 1.02000700000001, 1.008196, 1.01396099999999 ]);
        testVector2 = testVector.reshape(15, 2);
        testDataMode1 = np.array([ 1, 2, 2, 3, 4, 7, 9 ]);

        print(testVector2);
        print(testDataMode1);

        print("np.take()");
        testTake = np.take(testVector2, testDataMode1.astype(np.intp), axis=0);
        print(testTake);

        testVector3 = np.arange(0.0, 30000.0, 0.5, dtype= np.float64);
        testVector4 = testVector3.reshape(30000, 2);
        testIndex = np.arange(0, 30000, 100, dtype= np.intp);

        print("test BIG np.take()");
        # testBigTake = np.take(testVector4, testIndex, axis: 0);
        testBigTake = np.zeros((300, 2), dtype= np.float64);
        testBigTake = np.take(testVector4, testIndex, axis= 0);
        print(testIndex);
        print(testBigTake);
        print(np.diff(testIndex));
        print(np.diff(testBigTake, axis= 0));

    def test_flat_1(self):

        x = np.arange(10, 16).reshape(2,3);

        print(x)

        x.flat[3] = 9
        print(x)
        print(x.shape)
        print(x.strides)

        z = x.flat[3]
        print(z)
        print("")
        print("indexes")
        print("")

        for zz in x.flat:
            print(zz);

    def test_flat_2(self):

        x = np.arange(1, 7).reshape(2, 3)
        print(x)

        print(x.flat[3])

        print(x.T)
        print(x.T.flat[3])

        x.flat = 3
        print(x)

        x.flat[[1,4]] = 1
        print(x)

    def test_intersect1d_1(self):

        a = np.array([ 1, 3, 4, 3 ])
        b = np.array([ 3, 1, 2, 1 ])

        c = np.intersect1d(a,b)
        print(c)

    def test_setxor1d_1(self):

        a = np.array([1, 2, 3, 2, 4])
        b = np.array([2, 3, 5, 7, 5])
        c = np.setxor1d(a,b)

        print(c)

    def test_in1d_1(self):

        test = np.array([0, 1, 2, 5, 0])
        states = [0, 2]
        mask = nptest.in1d(test, states)
        print(mask)
        print(test[mask])

        mask = np.in1d(test, states, invert=True)
        print(mask)
        print(test[mask])

    def test_isin_1(self):

        element = 2*np.arange(4).reshape((2, 2));
        print(element)

        test_elements = [1, 2, 4, 8]
        mask = np.isin(element, test_elements)
        print(mask)
        print(element[mask])

        print("***********")

        mask = np.isin(element, test_elements, invert=True)
        print(mask)
        print(element[mask])

    def test_union1d_1(self):

        a = np.union1d([-1, 0, 1], [-2, 0, 2])
        print(a)

    def test_Ellipsis_indexing_1(self):

        a = np.array([10.0, 7, 4, 3, 2, 1])
        b = a[..., -1]
        print(b)
        print("********")

        a = np.array([[10.0, 7, 4], [3, 2, 1]])
        c = a[..., -1]
        print(c)
        print("********")

        TestData = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 ]
        a = np.array(TestData, dtype= np.uint32).reshape((1, 3, 2, -1, 1));
        d = a[..., -1]
        print(d)
        print("********")

        e = a[0, ..., -1]
        print(e)
        print("********")

        f = a[0, :,:,:, -1]
        print(f)
        print("********")

        g = a[0, 1, ..., -1]
        print(g)
        print("********")

        h = a[0, 2, 1, ..., -1]
        print(h)
        print("********")

        i = a[:, 2, 1, 1, ...]
        print(i)

    def test_concatenate_1(self):

        a = np.array([[1, 2], [3, 4]])
        b = np.array([[5, 6]])
        c = np.concatenate((a, b), axis=0)
        print(c)

        d = np.concatenate((a, b.T), axis=1)
        print(d)

        e = np.concatenate((a, b), axis=None)
        print(e)

        f = np.concatenate((np.eye(2), np.ones((2, 2))), axis = 0)
        print(f)

        g = np.concatenate((np.eye(2), np.ones((2, 2))), axis = 1)
        print(g)

    def test_concatenate_2(self):

        #a = np.array([1, 2, 3, 4, 5,6])
        #c = np.concatenate(a, axis=0)
        #d = np.concatenate((a), axis=0)
        #print(a.shape)
        #print(c.shape)
        #print(d.shape)
        #print("**********")

        a = np.array([[[[1, 2], [3, 4], [5,6]]]])
        c = np.concatenate(a, axis=0)
        d = np.concatenate((a), axis=0);
        print(a.shape)
        print(c.shape)
        print(d.shape)
        print("**********")

        a = np.array([[[[1, 2], [3, 4], [5,6]]]]).reshape((2,3,1,1))
        c = np.concatenate(a, axis=0)
        d = np.concatenate((a[0], a[1]), axis=0)

        print(a.shape)
        print(c.shape)
        print(d.shape)
        print("**********")

        a = np.array([[[[1, 2], [3, 4], [5,6]]]])
        c = np.concatenate(a, axis=1)
        d = np.concatenate((a[0]), axis=0);
        print(a.shape)
        print(c.shape)
        print(d.shape)
        print("**********")

        a = np.array([[[[1, 2], [3, 4], [5,6]]]]).reshape((3,2,1,1))
        c = np.concatenate(a, axis=1)
        d = np.concatenate((a[0], a[1], a[2]), axis=1)
        print(a.shape)
        print(c.shape)
        print(d.shape)

        #d = np.concatenate(a, axis=1)
        #print(d)

        #e = np.concatenate(a, axis=None)
        #print(e)

    def test_concatenate_3(self):

        a = np.array([[[[1, 2], [3, 4], [5,6]]]])
        c = np.concatenate(a, axis=-1)
        print(c)

        d = np.concatenate(a, axis=-2)
        print(d)

        c = np.concatenate((a,a,a), axis=-1)
        print(c)

        d = np.concatenate((a,a,a), axis=-2)
        print(d)

    def test_newaxis_ufunc_1(self):

        x = np.arange(0, 4, 1, float)
        y = x.reshape(4,1) * 4
        z = x + y
        print(z.shape)
        print(z)
        print("*************")

        y =  x.reshape(4,1,1) * 4
        z = x + y
        print(z.shape)
        print(z)
        print("*************")

        y =  x.reshape(4,1,1,1) * 4
        z = x + y
        print(z.shape)
        print(z)
        print("*************")

        y =  x.reshape(1,4,1) * 4
        z = x + y
        print(z.shape)
        print(z)
        print("*************")

        y =  x.reshape(1,4,1, 1) * 4
        z = x + y
        print(z.shape)
        print(z)
        print("*************")

    def test_newaxis_ufunc_2(self):

        x = np.arange(0, 4, 1, dtype=np.float32).reshape(2,2);
        y = x.reshape(2,2,1) * 4
        z = x + y
        print(z.shape)
        print(z)
        print("*************")

        y =  x.reshape(2,2,1,1) * 4
        z = x + y
        print(z.shape)
        print(z)
        print("*************")

        y =  x.reshape(2,2,1,1,1) * 4
        z = x + y
        print(z.shape)
        print(z)
        print("*************")

        y =  x.reshape(1,2,2,1) * 4
        z = x + y
        print(z.shape)
        print(z)
        print("*************")

        y =  x.reshape(1,2,2,1, 1) * 4
        z = x + y
        print(z.shape)
        print(z)
        print("*************")

    def test_newaxis_ufunc_3(self):

        x1 = np.array([1, 2, 3, 4, 5])
        x2 = np.array([5, 4, 3])

        x1_new = x1[:, np.newaxis]
        z = x1_new + x2
        print(z)
        print("*************")

        x1_new = x1[np.newaxis,:, np.newaxis]
        z = x1_new + x2
        print(z)
        print("*************")

        x2_new = x2[:, np.newaxis]
        z = x1 + x2_new
        print(z)
        print("*************")

        x2_new = x2[:, np.newaxis, np.newaxis]
        z = x1 + x2_new
        print(z)

    def test_newaxis_ufunc_4(self):

        x1 = np.array([1, 2, 3, 4, 5])
        x2 = np.array([5, 4, 3])

        x2_new = x2[:, np.newaxis]
        z = x2_new + x1
        print(z)
        print("*************")

        x2_new = x2[np.newaxis,:, np.newaxis]
        z = x2_new + x1
        print(z)
        print("*************")

        x2_new = x2[:, np.newaxis]
        z = x2_new + x1
        print(z)
        print("*************")

        x2_new = x2[:, np.newaxis, np.newaxis]
        z =  x2_new + x1
        print(z)

    def test_fields_1(self):

        data = np.zeros(4, dtype={'names':('name', 'age', 'weight'),'formats':('U10', 'i4', 'f8')})
        print(data.dtype)

if __name__ == '__main__':
    unittest.main()


import numpy as np
class Solution:
    def partitionDisjoint(self, A: []) -> int:
        if A == None or len(A) < 1:return -1
        #max[i] the max number in 0..i
        max = [0]*len(A)
        for i in range(len(A)):
            if i == 0:
                max[i] = A[i]
            elif A[i] > max[i - 1]:
                max[i] = A[i]
            else:
                max[i] = max[i-1]
        #min[i] the min number in i..len-1
        min = [0]*len(A)
        for i in range(len(A)-1, -1, -1):
            if i == len(A)-1:
                min[i] = A[i]
            elif A[i] < min[i+1]:
                min[i] = A[i]
            else:
                min[i] = min[i+1]
        for i in range(0, len(A)-1):
            if max[i] <= min[i+1]:
                return i+1
        return -1

    def partitionDisjoint_test(self, A: []) -> int:
        if A == None or len(A) < 1:return -1
        for i in range(1, len(A)):
            if np.max(A[:i]) <= np.min(A[i:]):
                return i
        return -1

sol = Solution()
A = [1,1,1,0,6,12]
print(sol.partitionDisjoint(A))


import numpy as np
import scipy.linalg as sla

class Interpolator(object):

	"""Cubic Hermite Spline Interpolation"""

	def __init__(self, x, y):

		"""
		Public variables: locations (x) and values (y) of knots,
		derivatives (d) at knots, and quadratic (c) and cubic (b)
		term coefficients
		"""

		self.x = x
		self.y = y
		self.__h = x[1:] - x[:-1]
		self.__delta = (y[1:] - y[:-1]) / self.__h
		self.d = self.__set_derivatives(self.__h, self.__delta)
		self.c = (3.0 * self.__delta - 2.0 * self.d[0: -1] - \
				  self.d[1:]) / self.__h
		self.b = (self.d[0: -1] - 2.0 * self.__delta + \
				  self.d[1:]) / self.__h ** 2

	def __set_derivatives(self, h, delta):

		"""
		Calculate derivatives at each knot, using not-a-knot
		formalism (see www.mathworks.com/content/dam/mathworks/
		mathworks-dot-com/moler/interp.pdf)
		"""

		# dimension components
		n_diffs = len(h) + 1
		r_vec = np.zeros(n_diffs)
		a_mat = np.zeros((n_diffs, n_diffs))

		# form delta vector
		r_vec[0] = ((h[0] + 2.0 * (h[0] + h[1])) * h[1] * delta[0] + \
					h[0] ** 2 * delta[1]) / (h[0] + h[1])
		r_vec[1: -1] = 3.0 * (h[1:] * delta[0: -1] + \
							  h[0: -1] * delta[1:])
		r_vec[-1] = (h[-1] ** 2 * delta[-2] + \
					 (2.0 * (h[-2] + h[-1]) + h[-1]) * \
					 h[-2] * delta[-1]) / (h[-2] + h[-1])

		# form dense A matrix
		a_mat[0, 0] = h[1]
		a_mat[0, 1] = h[1] + h[0]
		for i in range(1, n_diffs - 1):
			a_mat[i, i-1] = h[i]
			a_mat[i, i] = 2.0 * (h[i-1] + h[i])
			a_mat[i, i+1] = h[i-1]
		a_mat[-1, -2] = h[-1] + h[-2]
		a_mat[-1, -1] = h[-2]

		# solve system and return
		return np.linalg.solve(a_mat, r_vec)

	def __set_derivatives_banded(self, h, delta):

		"""
		Calculate derivatives at each knot, using not-a-knot
		formalism and forming a banded (not dense) matrix
		"""

		# dimension components
		n_diffs = len(h) + 1
		r_vec = np.zeros(n_diffs)
		a_mat = np.zeros((n_diffs, n_diffs))

		# form delta vector
		r_vec[0] = ((h[0] + 2.0 * (h[0] + h[1])) * h[1] * delta[0] + \
					h[0] ** 2 * delta[1]) / (h[0] + h[1])
		r_vec[1: -1] = 3.0 * (h[1:] * delta[0: -1] + \
							  h[0: -1] * delta[1:])
		r_vec[-1] = (h[-1] ** 2 * delta[-2] + \
					 (2.0 * (h[-2] + h[-1]) + h[-1]) * \
					 h[-2] * delta[-1]) / (h[-2] + h[-1])

		# form banded A matrix
		a_mat_ud = np.concatenate(([0], [h[1] + h[0]], h[0: -1]))
		a_mat_d = np.concatenate(([h[1]], 2.0 * (h[0: -1] + h[1:]), [h[-2]]))
		a_mat_ld = np.concatenate((h[1:], [h[-1] + h[-2]], [0]))
		a_mat_band = np.matrix([a_mat_ud, a_mat_d, a_mat_ld])

		# solve system and return
		return sla.solve_banded((1, 1), a_mat_band, r_vec, \
								overwrite_ab=True, \
								overwrite_b=True)

	def __interpolate_scalar(self, x_int, der=False):

		"""Interpolate an individual point"""

		ind = [i for i, check in enumerate(x_int < self.x) if check][0] - 1
		s = x_int - self.x[ind]
		if der:
			y_int = self.d[ind] + \
					s * (2.0 * self.c[ind] + \
						 s * 3.0 * self.b[ind])
		else:
			y_int = self.y[ind] + \
					s * (self.d[ind] + \
						 s * (self.c[ind] + \
							  s * self.b[ind]))
		return y_int

	def interpolate(self, x_int):

		"""Public interpolation method, accepts scalars or 1D arrays"""

		if isinstance(x_int, np.ndarray):
			if (x_int < self.x[0]).any() or \
			   (x_int > self.x[-1]).any():
				raise ValueError('Requested interpolation out of bounds!')
			n = len(x_int)
			y_int = np.zeros(n)
			for i in range(n):
				y_int[i] = self.__interpolate_scalar(x_int[i])
		else:
			if x_int < self.x[0] or x_int > self.x[-1]:
				raise ValueError('Requested interpolation out of bounds!')
			y_int = self.__interpolate_scalar(x_int)
		return y_int


""" Scripts to run that plot crucial circuits """

import numpy as np
import matplotlib.pyplot as plt

from common_filters import LowPassFilter
from signals import Signal
from graph import Node, Edge, Graph
from components.diode import Diode
from components.resistor import Resistor
from components.capacitor import Capacitor
from config import Config
from units import Units
from util import irangef

def diode_iv_curve():
    """ Plots the IV curve of a diode. """
    plt.title("Diode IV Curve")
    plt.xlabel("Voltage (V)")
    plt.ylabel("Current (A)")

    # Plot ideal
    voltages, currents = [], []
    for v in irangef(0.6, 0.8, 0.01):
        graph = Graph()
        node_a = Node(graph, value=v, fixed=True)
        node_b = Node(graph, value=0, fixed=True)
        edge_i = Edge(graph, node_a, node_b)
        diode = Diode(graph, node_a, node_b, edge_i)
        graph.add_component(diode)
        graph.solve()
        voltages.append(v)
        currents.append(edge_i.value())

    plt.plot(voltages, currents, label="Voltage varies continuously")

    # Plot random
    SAMPLES = 1000

    currents = []
    voltages = np.random.random(SAMPLES) * 0.2 + 0.6
    graph = Graph()
    node_a = Node(graph, value=0, fixed=True)
    node_b = Node(graph, value=0, fixed=True)
    edge_i = Edge(graph, node_a, node_b)
    diode = Diode(graph, node_a, node_b, edge_i)
    graph.add_component(diode)

    for v in voltages:
        node_a.set_value(v)
        graph.solve()

        currents.append(edge_i.value())

    # Sort
    currents = (np.array(currents))[voltages.argsort()]
    voltages.sort()

    plt.plot(voltages, currents, label="Voltage varies randomly")
    plt.legend()
    plt.show()

def lowpass_filter_transfer_curve():
    """ Plots the transfer function of a lowpass filter with cutoff 500 Hz. """
    r = 318
    c = 1 * Units.u

    fs, gains = [], []
    for f in irangef(100, 1000, 50):
        lpf = LowPassFilter.make(r, c)
        input_signal = Signal.sine(0.01, f, 5)
        output_signal = lpf.execute(input_signal)

        gain = max(output_signal) / 5

        fs.append(f)
        gains.append(gain)

    cutoff_freq = LowPassFilter.cutoff_frequency(r, c)
    plt.title("Lowpass Transfer Curve (cutoff={0} Hz)".format(cutoff_freq))
    plt.xlabel("Frequency (Hz)")
    plt.ylabel("Gain")
    plt.plot(fs, gains)
    plt.show()

def capacitor_charge_curve():
    """ Plots the charging of a capacitor overtime. """
    r = 100
    c = 10 * Units.u

    DURATION = 0.01

    ts = irangef(0, DURATION, Config.time_step)
    ts = np.array(ts)

    # Model
    graph = Graph()
    node_5 = Node(graph, value=5, fixed=True, source=True)
    node_gnd = Node(graph, value=0, fixed=True, source=True)
    node = Node(graph)

    # Resistor
    edge = Edge(graph, node_5, node)
    graph.add_component(Resistor(graph, r, node_5, node, edge))
    # Capacitor
    edge = Edge(graph, node, node_gnd)
    graph.add_component(Capacitor(graph, c, node, node_gnd, edge))

    vs = []
    for t in ts:
        graph.solve()

        v = node.value()

        np.append(ts, t)
        vs.append(v)

    plt.title("Capacitor Charge Curve (R = 100 Ohms, C = 10 uF, V = 5 V)")
    plt.xlabel("Time (s)")
    plt.ylabel("Voltage (V)")
    plt.plot(ts, vs, label="Model")

    # Plot ideal
    vs_ideal = 5 * (1 - np.exp(-ts / (r * c)))
    plt.plot(ts, vs_ideal, label="Ideal")

    plt.legend()
    plt.show()

if __name__ == "__main__":
    #diode_iv_curve()
    #lowpass_filter_transfer_curve()
    capacitor_charge_curve()


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

def feval(funcName, *args):
    return eval(funcName)(*args)

def RK4thOrder(func, yinit, x_range, h):
    m = len(yinit)
    n = int((x_range[-1] - x_range[0]) / h)

    x = x_range[0]
    y = yinit

    # Containers for solutions
    xsol = np.empty(0)
    xsol = np.append(xsol, x)

    ysol = np.empty(0)
    ysol = np.append(ysol, y)

    for i in range(n):
        k1 = feval(func, x, y)

        yp2 = y + k1 * (h / 2)

        k2 = feval(func, x + h / 2, yp2)

        yp3 = y + k2 * (h / 2)

        k3 = feval(func, x + h / 2, yp3)

        yp4 = y + k3 * h

        k4 = feval(func, x + h, yp4)

        for j in range(m):
            y[j] = y[j] + (h / 6) * (k1[j] + 2 * k2[j] + 2 * k3[j] + k4[j])

        x = x + h
        xsol = np.append(xsol, x)

        for r in range(len(y)):
            ysol = np.append(ysol, y[r])

    return [xsol, ysol]

def myFunc(t, vectorY):
    df = np.zeros((len(vectorY)))

    y = vectorY[0]
    z = vectorY[1]

#########################       INPUT GOES HERE

    # y' = z
    df[0] = z
    # z' = ty^2(1+sin(t)y) + yt^3
    df[1] = t*y**2*(1+math.sin(t)*y) + t**3*y
    return df

h = 0.01    # độ dài 1 bước
t0 = 0      # điểm bắt đầu
tn = 1      # điểm kết thúc

y0 = 1      # giá trị đầu y(t0)
z0 = 1      # giá trị đầu z(t0)

# this work with more than 2 equation ...

##########################       \INPUT GOES HERE

y0 = float(y0)
z0 = float(z0)

x = np.array([t0, tn])
yinit = np.array([y0, z0])

[ts, ys] = RK4thOrder('myFunc', yinit, x, h)

node = len(yinit)
ys1 = ys[0::node]
ys2 = ys[1::node]

##########################       PRINT RESULT

for i in range(len(ts)):
    print("t = ", ts[i])
    print("y1 = ", ys1[i], ";   y2 = ", ys2[i])

##########################       \PRINT RESULT

##########################       Plot y, z with respect to t  ( but currently only y, uncomment plt.plot... below to plot z )

plt.plot(ts, ys1, 'r')
#plt.plot(ts, ys2, 'b')
plt.xlim(x[0], x[1])
plt.legend(["y(1)", "y(2)"], loc=2)
plt.xlabel('x', fontsize=17)
plt.ylabel('y', fontsize=17)
plt.tight_layout()
plt.show()

##########################       \Plot y, z with respect to t

##########################       plot y - z as x - y(x)

plt.plot(ys1, ys2, 'r')
plt.xlabel('con mồi', fontsize=17)
plt.ylabel('thú săn mồi', fontsize=17)
plt.show()

##########################       \plot y1 - y2

# Uncomment the following to print the figure:
#plt.savefig('Fig_ex7_RK4th.png', dpi=600)

Topic 5 - Solving Differential Equations/28.Runge_Kutta/R-K system of equation/RK4.py


from mpl_toolkits.mplot3d import Axes3D
from matplotlib import cm
from matplotlib.ticker import LinearLocator, FormatStrFormatter
import matplotlib.pyplot as plt
import numpy as np

def plotSymFactor3D(alpha):
    fig = plt.figure()
    ax = fig.gca(projection='3d')
    X = np.arange(-np.pi, np.pi+0.001, 0.1)
    Y = np.arange(-np.pi, np.pi+0.001, 0.1)
    X, Y = np.meshgrid(X, Y)
    Z = np.absolute(np.sin(X) + float(alpha)*np.sin(Y))
    surf = ax.plot_surface(X, Y, Z, rstride=1, cstride=1, cmap=cm.coolwarm,
            linewidth=0, antialiased=False)
    ax.set_zlim(0.0, 2.0)

    ax.zaxis.set_major_locator(LinearLocator(10))
    ax.zaxis.set_major_formatter(FormatStrFormatter("%.02f"))

    plt.show()

def plotSymFactor(alpha, fname, largeFont):
    X = np.arange(-np.pi, np.pi+0.001, 0.01)
    Y = np.arange(-np.pi, np.pi+0.001, 0.01)
    X, Y = np.meshgrid(X, Y)
    Z = np.absolute(np.sin(X) + float(alpha)*np.sin(Y))

    fig = plt.figure()

    if largeFont:
        plt.xlabel("$k_x$", fontsize=20)
        plt.ylabel("$k_y$", fontsize=20)
        plt.xticks(fontsize=20)
        plt.yticks(fontsize=20)
    else:
        plt.xlabel("$k_x$")
        plt.ylabel("$k_y$")

    N = 30
    p = plt.contourf(X, Y, Z, N, cmap=cm.binary)

    cb = fig.colorbar(p)

    if largeFont:
        cb.ax.tick_params(labelsize=20)

    plt.savefig(fname + ".png", bbox_inches="tight", dpi=200)

if __name__ == "__main__":
    plotSymFactor(-1, "sym_alpha_m1", False)
    plotSymFactor(1, "sym_alpha_p1", False)


# -*- coding: utf-8 -*-

# =============================================================================
# Loading Modules
# =============================================================================
import numpy as np
from envs.GridWorld import GridworldEnv
import matplotlib.pyplot as pl

# =============================================================================
# Create Environment
# =============================================================================
env = GridworldEnv()

# =============================================================================
# Evaluate Policy
# =============================================================================

def policy_eval(policy, env, discount_factor=1.0, theta=0.00001):
    """
    Evaluate a policy given an environment and a full description of the environment's dynamics.

    Args:
        policy: [S, A] shaped matrix representing the policy.
        env: OpenAI env. env.P represents the transition probabilities of the environment.
            env.P[s][a] is a list of transition tuples (prob, next_state, reward, done).
            env.nS is a number of states in the environment.
            env.nA is a number of actions in the environment.
        theta: We stop evaluation once our value function change is less than theta for all states.
        discount_factor: Gamma discount factor.

    Returns:
        Vector of length env.nS representing the value function.
    """
    # Start with a random (all 0) value function
    V = np.zeros(env.nS)
    k=0
    diff=1
    errors=[]
    historicalCheck=[0,10]
    historical=[]
    while (diff>theta):
        newV=np.zeros(env.nS)
        for s in range(env.nS):
            temp1=0
            for a in range(env.nA):
                temp2=0
                for sp in range(len(env.P[s][a])):
                    transition=env.P[s][a][sp]
                    temp2+=transition[0]*(transition[2]+discount_factor*V[transition[1]])
                    #print('action ',a, 'temp ',temp2)
                temp1+=policy[s][a]*temp2
            newV[s]=temp1
        diff=np.abs(np.subtract(V, newV)).mean()
        errors+=[diff]
        V=newV
        if k in historicalCheck:
            historical+=[V]
        k+=1
        print('iteration = ',k,'  delta = %.3e'%diff)

    return (np.array(V),errors,historical)

# =============================================================================
# Evaluate a random policy
# =============================================================================
random_policy = np.ones([env.nS, env.nA]) / env.nA
v,errors,historical = policy_eval(random_policy, env)

# =============================================================================
# Checking the results
# =============================================================================
expected_v = np.array([0, -14, -20, -22, -14, -18, -20, -20, -20, -20, -18, -14, -22, -20, -14, 0])
np.testing.assert_array_almost_equal(v, expected_v, decimal=2)

# =============================================================================
# Display the results
# =============================================================================
print("computed Value Function:")
print(v.reshape(env.shape))
print("")

print("expected Value Function:")
print(expected_v.reshape(env.shape))
print("")

fig=pl.figure()
pl.plot(errors)
pl.xlabel('iteration')
pl.ylabel('L1 difference of deltas')
pl.title('evolution of the Value function deltas at each iteration')
pl.savefig('PolicyEvaluation.png')
pl.show()

fig=pl.figure(figsize=(12,5))
ax=fig.add_subplot(141)
ax.imshow(historical[0].reshape(env.shape))
pl.title('iteration 0')

ax=fig.add_subplot(142)
ax.imshow(historical[1].reshape(env.shape))
pl.title('iteration 10')

ax=fig.add_subplot(143)
ax.imshow(v.reshape(env.shape))
pl.title('final iteration - {}'.format(len(errors)))

ax=fig.add_subplot(144)
ax.imshow(expected_v.reshape(env.shape))
pl.title('expected value function')

pl.savefig('EvolutionPolicyEvaluation.png')

pl.legend()
pl.show()


import logging
import sys
import numpy as np
import matplotlib.pyplot as plt

def load_file(filename):
    """
    Carrega o dataset para a memória
        :param filename: nome do arquivo

        :return: matriz x e y
    """
    x_input = []
    y_input = []
    f = open("data/" + filename + ".txt")
    lines = f.readlines()

    for line in lines:
        if filename == "Books_attend_grade":
            x0, x1, y = line.split(";")
            x_input.append([float(x0), float(x1)])
            y_input.append(float(y))
        else:
            line = line.strip("\n")
            x, y = line.split(";")
            x_input.append(float(x))
            y_input.append(float(y))

    return x_input, y_input

def subtract_mean(x, y):
    """
    Subtrair a média de todo o dataset
        :param x: entrada x
        :param y: entrada y

        :return: matriz ajustada
    """
    if isinstance(x[0], list):
        x_zipped = list(zip(*x))
        x1_mean = sum(x_zipped[0]) / len(x_zipped[0])
        x2_mean = sum(x_zipped[1]) / len(x_zipped[1])
    else:
        x_mean = sum(x) / len(x)

    y_mean = sum(y) / len(y)
    x_adjusted = []
    y_adjusted = []

    for i in range(len(x)):
        if isinstance(x[0], list):
            x_adjusted.append([x[i][0] - x1_mean, x[i][1] - x2_mean])
        else:
            x_adjusted.append(x[i] - x_mean)

        y_adjusted.append(y[i] - y_mean)

    return x_adjusted, y_adjusted

def covariance(a, b):
    """
    Calcula a covariancia entre dois pontos
        :param a: ponto a
        :param b: ponto b

        :return: covariancia entre a e b
    """
    covariance_result = 0

    for i in range(len(a)):
        covariance_result += (a[i] * b[i]) / (len(a) - 1)

    return covariance_result

def covariance_matrix(x, y):
    """
    Calcula matriz de covariancia
        :param x: entrada x
        :param y: entrada y

        :return: matriz de covariancia
    """
    cov_matrix = []

    if not isinstance(x[0], list):
        cov_x_x = covariance(x, x)
        cov_x_y = covariance(x, y)
        cov_matrix.append([cov_x_x, cov_x_y])
        cov_y_x = covariance(y, x)
        cov_y_y = covariance(y, y)
        cov_matrix.append([cov_y_x, cov_y_y])
    else:
        x_zipped = list(zip(*x))
        x1 = x_zipped[0]
        x2 = x_zipped[1]
        cov_x_x = covariance(x1, x1)
        cov_x_y = covariance(x1, y)
        cov_x_x2 = covariance(x1, x2)
        cov_matrix.append([cov_x_x, cov_x_y, cov_x_x2])
        cov_y_x = covariance(y, x1)
        cov_y_y = covariance(y, y)
        cov_y_x2 = covariance(y, x2)
        cov_matrix.append([cov_y_x, cov_y_y, cov_y_x2])
        cov_x2_x = covariance(x2, x1)
        cov_x2_y = covariance(x2, y)
        cov_x2_x2 = covariance(x2, x2)
        cov_matrix.append([cov_x2_x, cov_x2_y, cov_x2_x2])

    return cov_matrix

def calc_pca(dataset):
    x, y = load_file(dataset)
    logging.info("*******INPUT*******")
    logging.info(x)
    logging.info(y)

    logging.info("*******MEAN SUBTRACTED*******")
    x_minus_mean, y_minus_mean = subtract_mean(x, y)
    logging.info(x_minus_mean)
    logging.info(y_minus_mean)

    logging.info("*******COVARIANCE MATRIX*******")
    cov = covariance_matrix(x_minus_mean, y_minus_mean)
    logging.info(cov)

    logging.info("*******AUTOVALOR / AUTOVETOR*******")
    cov_np = np.array(cov)
    w, v = np.linalg.eig(cov_np)
    logging.info(w)
    logging.info(v)

    logging.info("*******COMPONENTES*******")
    feature_vector = v[:, np.argmax(w)]
    gradient_1 = feature_vector[1] / feature_vector[0]
    feature_vector_2 = v[:, np.argmin(w)]
    gradient_2 = feature_vector_2[1] / feature_vector_2[0]
    logging.info(feature_vector)

    logging.info("*******PLOT*******")
    plt.plot(x_minus_mean, y_minus_mean, '+', color='black', label='Dados ajustados')
    plt.plot(0, 0, 'x', color='blue', label='Média')
    min_x = min(x_minus_mean)
    max_x = max(x_minus_mean)
    pca_1_x = [min_x, 0, max_x]
    pca_1_y = [min_x * gradient_1, 0, max_x * gradient_1]
    plt.plot(pca_1_x, pca_1_y, color='red', label='Componente principal')

    pca_2_x = [min_x, 0, max_x]
    pca_2_y = [min_x * gradient_2, 0, max_x * gradient_2]
    plt.plot(pca_2_x, pca_2_y, color='orange', label='Componente secundário')

    if dataset == "US_Census":
        x_mq = 2010 - (sum(x) / len(x))
        y_mq = 286.9128 - (sum(y) / len(y))
        plt.plot([x_minus_mean[0], x_mq], [y_minus_mean[0], y_mq], color='purple', label='Mínimos Quadrados')

    if dataset == "alpswater":
        x_mq = 31.06 - (sum(x) / len(x))
        y_mq = 214.3658 - (sum(y) / len(y))
        plt.plot([x_minus_mean[0], x_mq], [y_minus_mean[0], y_mq], color='purple', label='Mínimos Quadrados')

    plt.legend(loc='upper center', bbox_to_anchor=(0.5, -0.05))
    plt.tight_layout()
    plt.title("Dataset: " + dataset)
    plt.show()

logging.basicConfig(stream=sys.stderr, level=logging.INFO)

calc_pca("alpswater")

calc_pca("US_Census")


"""
A magic square is a square of size NxN with the numbers 1 through n2 put in so that all rows, all columns and both
diagonals sum to the same number. For instance, this is a 3x3 magic square:

8 1 6
3 5 7
4 9 2

As you can see all rows, all columns and both diagonals (8+5+2 and 4+5+6) sum to the same number, 15.

Write a program that draws a magic square of size 18x18.

    Thanks to /u/SwimmingPastaDevil for submitting this problem in /r/dailyprogrammer_ideas! And on behalf of the
    moderators, I'd like to thank everyone who submitted problems the last couple of days, it's been really helpful,
    and there are some great problems there! Keep it up, it really helps us out a lot!

"""

import numpy as np

def magic_square_4(n):
    arr = np.arange(1, n*n + 1)
    arr.resize(n, n)
    n //= 4
    mask = np.array([[0, 1, 1, 0],
                     [1, 0, 0, 1],
                     [1, 0, 0, 1],
                     [0, 1, 1, 0]])
    mask = np.tile(mask, (n, n))
    antimask = 1 - mask

    return (mask * arr) + np.rot90((antimask * arr), 2)

def siamese_square(n, start=1):
    arr = np.zeros((n, n), dtype=int)
    x, y = 0, n // 2
    arr[x, y] = start
    for i in range(1+start, (n**2) + start):
        x, y = (x-1) % n, (y+1) % n
        if arr[x, y]:
            x, y = (x+2) % n, (y-1) % n
        arr[x, y] = i
    return arr

def medjig():
    size = 3
    arr = np.arange(0, size+1)
    np.random.shuffle(arr)
    arr.resize(2, 2)
    return arr

def medjig_creator(n):
    s = n // 2

    target = 3 * s
    retry = False
    while True:
        med = np.array([0])

        for _1 in range(s):
            temp = medjig()
            for _2 in range(s - 1):
                temp = np.concatenate((temp, medjig()), 1)
            for t in temp:
                if sum(t) != target:
                    # print(sum(t), target)
                    retry = True
                    break
            if retry:
                break
            if med.any():
                med = np.concatenate((med, temp), 0)
            else:
                med = temp[:]
        if retry:
            continue
        if validate_square(med):
            break

    return med

def concat_siamese(n):
    return np.repeat(np.repeat(siamese_square(n), 2, 0), 2, 1)

def magic_square_2(n):
    siam = concat_siamese(n//2)
    med = medjig_creator(n)

    res = np.zeros((n, n), dtype=int)
    for a in range(n):
        for b in range(n):
            res[a, b] = siam[a, b] + (9 * med[a, b])
    return res

def magic_square_2nd(n):
    """ http://www.math.wichita.edu/~richardson/mathematics/magic%20squares/even-ordermagicsquares.html """
    size = n // 2
    start = size * size
    arr0 = np.concatenate((siamese_square(size), siamese_square(size, (2 * start) + 1)), 1)
    arr1 = np.concatenate((siamese_square(size, (3 * start) + 1), siamese_square(size, start + 1)), 1)
    res = np.concatenate((arr0, arr1), 0)

    exchanger = (n - 2) // 4
    for x in range(exchanger-1):
        res[:, x] = np.roll(res[:, x], size, 0)
        res[:, n - x - 1] = np.roll(res[:, n - x - 1], size, 0)
    res[:, exchanger-1] = np.roll(res[:, exchanger-1], size, 0)
    res[n//4, exchanger-1], res[(3*n)//4, exchanger-1] = res[(3*n)//4, exchanger-1], res[n//4, exchanger-1]
    res[n//4, exchanger], res[(3*n)//4, exchanger] = res[(3*n)//4, exchanger], res[n//4, exchanger]
    return res

def validate_square(sq):
    s = sum(np.diagonal(sq, axis1=1, axis2=0))
    for column in sq:
        if s != sum(column):
            return False
    for row in np.rot90(sq):
        if s != sum(row):
            return False
    return s

def main():
    # required: n >= 3
    n = 18
    magic = -1
    if n % 4 == 0:
        magic = magic_square_4(n)
    elif n % 2 != 0:
        magic = siamese_square(n)
    elif n % 2 == 0:
        # magic = magic_square_2(n)
        magic = magic_square_2nd(n)
    print(magic, validate_square(magic))

if __name__ == "__main__":
    main()


import numpy as np
from RNA.FuncoesAtivacao import sigmoide, identidade
from RNA.FuncoesCusto import mse

def treinar(X, Y, **kwargs):
  taxa_aprendizado = kwargs.get("taxa_aprendizado", 0.02)
  numero_epocas = kwargs.get("numero_epocas", 100)
  batch = kwargs.get("batch", 100)
  funcao_custo = kwargs.get("funcao_custo", mse)
  funcao_ativacao = kwargs.get("funcao_ativacao", sigmoide)
  inicializacao_media = kwargs.get("inicializacao_media", 0)
  inicializacao_desvio = kwargs.get("inicializacao_desvio", 0.5)

  #nº de variáveis de entrada
  num_in = X.shape[1]

  #nº de variáveis de saída
  num_out = Y.shape[1]

  #nº de instâncias
  num_inst = X.shape[0]

  #Inicialização dos parâmetros
  W = [np.random.normal(inicializacao_media,inicializacao_desvio,num_in) for k in range(num_out)]
  B = [np.random.normal(inicializacao_media,inicializacao_desvio,1) for k in range(num_out)]

  log_erros = []

  for epocas in np.arange(numero_epocas):

    for z in np.arange(batch):

      i = np.random.randint(0,num_inst)

      erros = []

      for saida in range(num_out):

        net = W[saida].dot(X[i,:]) + B[saida]  # WX + B

        estimativa = funcao_ativacao(net)  # Ŷ = sigmoide(net)

        erros.append(funcao_custo(Y[i,saida],estimativa))  # Armazena o erro

        # Regra Delta - O valor de atualização dos pesos
        delta = funcao_custo(Y[i,saida],estimativa, derivada=True) * funcao_ativacao(net, derivada = True)

        #Atualiza os pesos (somo cada peso com seu delta)
        W[saida] += taxa_aprendizado*-delta*-X[i,:]

        #Atualização do viés
        B[saida] += taxa_aprendizado*-delta

    log_erros.append(np.mean(erros))   # Retorna o histórico de erros para visualização

  return W, B, log_erros

def treinar_batch(X, Y, **kwargs):
  taxa_aprendizado = kwargs.get("taxa_aprendizado", 0.02)
  numero_epocas = kwargs.get("numero_epocas", 100)
  batch = kwargs.get("batch", 100)
  funcao_custo = kwargs.get("funcao_custo", mse)
  funcao_ativacao = kwargs.get("funcao_ativacao", sigmoide)
  inicializacao_media = kwargs.get("inicializacao_media", 0)
  inicializacao_desvio = kwargs.get("inicializacao_desvio", 0.5)

  num_in = X.shape[1]
  num_out = Y.shape[1]
  num_inst = X.shape[0]
  W = [np.random.normal(inicializacao_media,inicializacao_desvio,num_in) for k in range(num_out)]
  B = [np.random.normal(inicializacao_media,inicializacao_desvio,1) for k in range(num_out)]
  log_erros = []

  for epocas in np.arange(numero_epocas):
    x_avg = np.zeros(num_in)
    delta_avg = np.zeros(num_out)

    erros = []
    for z in np.arange(batch):
      i = np.random.randint(0,num_inst)
      x_avg += X[i,:]
      for saida in range(num_out):
        net = W[saida].dot(X[i,:]) + B[saida]  # WX + B
        estimativa = sigmoide(net)  # Ŷ = sigmoide(net)
        erros.append(funcao_custo(Y[i,saida],estimativa))  # Armazena o erro

        # Regra Delta - O valor de atualização dos pesos
        delta = funcao_custo(Y[i,saida],estimativa, derivada=True) * funcao_ativacao(net, derivada = True)
        delta_avg[saida] += delta
    x_avg /= batch
    delta_avg /= batch
    for saida in range(num_out):
      W[saida] += taxa_aprendizado*-delta_avg[saida]*-x_avg
      B[saida] += taxa_aprendizado*-delta_avg[saida]

    log_erros.append(np.mean(erros))   # Retorna o histórico de erros para visualização

  return W, B, log_erros

def classificacao(x,W,B, **kwargs):
  funcao_ativacao = kwargs.get("funcao_ativacao", sigmoide)
  num_inst = x.shape[0]
  num_in = x.shape[1]
  num_out = len(W)
  ret = []
  for i in range(num_inst):
    tmp = [funcao_ativacao(W[modelo].dot(x[i,:]) + B[modelo])[0] for modelo in np.arange(num_out)]
    tmp = np.where(tmp == max(tmp), 1, 0).argmax()
    ret.append(tmp)
  return np.array(ret)

def regressao(x,W,B,**kwargs):
  funcao_ativacao = kwargs.get("funcao_ativacao", identidade)
  num_inst = x.shape[0]
  num_in = x.shape[1]
  num_out = len(W)
  ret = []
  for i in range(num_inst):
    tmp = [funcao_ativacao(W[modelo].dot(x[i,:]) + B[modelo])[0] for modelo in np.arange(num_out)]
    tmp = np.where(tmp == max(tmp), 1, 0).argmax()
    ret.append(tmp)
  return np.array(ret)


import scipy.stats
import torch
import statsmodels.api as sm
import numpy as np

class LinearRegSolver:
    def __init__(self):
        self.info = 'Fit y ~ x + covariates for every x_i ~ y pair.'

    @staticmethod
    def solve(X, y, C, device=None):
        q, _ = torch.qr(C)
        y_ = y - torch.matmul(q, torch.matmul(q.T, y))
        X_ = X - torch.matmul(q, torch.matmul(q.T, X))
        xty = torch.matmul(X_.T, y_)
        xtx = torch.pow(X_, 2).sum(axis=0)
        bhat = xty / xtx
        dof = X.shape[0] - C.shape[1] - 1
        sigma2 = torch.pow(torch.unsqueeze(y_, 1) - torch.einsum('ij,j->ij', X_, bhat), 2).sum(axis=0) / dof
        se = torch.sqrt(sigma2 / xtx)

        bhat = bhat.numpy()
        se = se.numpy()

        return bhat, se, dof

    @staticmethod
    def stat_test(bhat, se, dof):
        tval = bhat / se
        pval = scipy.stats.t.sf(np.abs((bhat / se)), df=dof) * 2
        return pval, tval

def test_solver(y, x, covar, return_val=False):
    '''
    Input y, x, covar are numpy array
    y: n x 1
    x: n x k
    covar: n x p
    Return: k bhat's and se's by LinearRegSolver and by statsmodels.api.OLS
    '''
    print('---- Testing LinearRegSolver ----')
    # LinearRegSolver
    solver = LinearRegSolver()
    bhat, se, dof = solver.solve(torch.Tensor(x), torch.Tensor(y), torch.Tensor(covar))
    pval, stat = solver.stat_test(bhat, se, dof)

    # statsmodels
    K = x.shape[1]
    bhat2 = np.zeros((K))
    stat2 = np.zeros((K))
    pval2 = np.zeros((K))
    for k in range(K):
        mod = sm.OLS(y, np.concatenate((x[:, k:(k+1)], covar), axis=1))
        res = mod.fit()
        bhat2[k] = res.params[0]
        stat2[k] = res.tvalues[0]
        pval2[k] = res.pvalues[0]
    se2 = bhat2 / stat2
    print('bhat mean abs difference = {}'.format(np.abs((bhat - bhat2)).mean()))
    print('se mean abs difference = {}'.format(np.abs((se - se2)).mean()))
    print('tval mean abs difference = {}'.format(np.abs((stat - stat2)).mean()))
    print('log p mean abs difference = {}'.format(np.abs((np.log(pval) - np.log(pval2))).mean()))

    if return_val is True:
        return bhat, pval, bhat2, pval2

import numpy,import scipy,import statsmodels


# ex1_euler
import numpy as np
import matplotlib.pyplot as plt

# initial
t = 0
y_0 = 1
dt = 0.1        # change dt to compare

# euler
t_ary = [t]
y_ary = [y_0]

while t < 5:
    y_1 = y_0 + y_0 * dt
    t += dt

    t_ary.append(t)
    y_ary.append(y_1)

    y_0 = y_1

# analytic
t_ary_analytic = np.arange(0, 5.1, 0.1)
y_ary_analytic = np.exp(t_ary_analytic)

# plot
plt.plot(t_ary, y_ary, label="euler")
plt.plot(t_ary_analytic, y_ary_analytic, label="analytic")
plt.legend()
plt.show()