# -*- coding: utf-8 -*-
"""
Created on Thu Mar 29 11:39:05 2018

@author: garvit
"""

import scipy.stats as ss
from decimal import Decimal
import math as m
import numpy as np
from datetime import date,timedelta
from mibian import BS

def call_bsm (S0,K,r,T,Otype,sig):
    d1 = Decimal(m.log(S0/K)) + (r+ (sig*sig)/2)*T/(sig*Decimal(m.sqrt(T)))
    d2 = d1 - sig*Decimal(m.sqrt(T))
    if (Otype == "Call"):
        price = S0*Decimal(ss.norm.cdf(np.float(d1))) \
        - K*Decimal(m.exp(-r*T))*Decimal(ss.norm.cdf(np.float(d2)))
        return (price)
    elif (Otype == "Put"):
        price  = -S0*Decimal(ss.norm.cdf(np.float(-d1)))\
        + K*Decimal(m.exp(-r*T))*Decimal(ss.norm.cdf(np.float(-d2)))
        return (price)

def vega (S0,K,r,T,sig):
    d1 = Decimal(m.log(S0/K))/(sig*Decimal(m.sqrt(T))) + Decimal((r+ (sig*sig)/2)*T/(sig*Decimal(m.sqrt(T))))
    vega = S0*Decimal(ss.norm.pdf(np.float(d1)))*Decimal(m.sqrt(T))
    return(vega)

def imp_vol(S0, K, T, r, market,flag):
    e = 10e-15; x0 = Decimal(1);
    def newtons_method(S0, K, T, r, market,flag,x0, e):
        delta = call_bsm (S0,K,r,T,flag,x0) - market
        while delta > e:
            x0 = Decimal(x0 - (call_bsm (S0,K,r,T,flag,x0) - market)/vega (S0,K,r,T,x0))
            delta = abs(call_bsm (S0,K,r,T,flag,x0) - market)
        return(Decimal(x0))
    sig =  newtons_method(S0, K, T, r, market,flag,x0 , e)
    return(sig*100)

fromdate = date(2018,3,30)
todate = date(2018,9,21)
daygenerator = (fromdate + timedelta(x + 1) for x in range((todate - fromdate).days))
days = sum(1 for day in daygenerator if day.weekday())
T = Decimal(days)/Decimal(360)
S0 = Decimal(109.97)
K = Decimal(100)
market = Decimal(13.725)
flag = 'Call'
r = Decimal(0.0225)
iv = imp_vol(S0, K, T, r, market,flag)
print("")
print("")
print("############################ Newton Raphson Method ####################")
print("")
print('The implied volatility from my calculation is',iv,"%")
cp_iv = call_bsm(S0,K,r,T,Otype="Call",sig=Decimal(iv/100))
print('Newton Raphson implied volatility gives call price',cp_iv)
print("Error in obtained call price", '{:.20f}'.format(np.float(market - cp_iv)))
# Mibian Library
c = BS([S0, K, r, days], callPrice=market)
print("")
print("")
print("############################ Bisection Method #########################")
print("")
print("The implied volatility using Mibian Library is", c.impliedVolatility,"%")
cp_mbian = call_bsm(S0,K,r,T,Otype="Call",sig=Decimal(c.impliedVolatility/100))
print('Mibian implied volatility gives call price',cp_mbian)
print("Error in obtained call price", market - cp_mbian)


import numpy as np

def evaluate(y_pred: np.array, y_true: np.array, precision=4):
    print('MSE:', round(MSE(y_pred, y_true), precision))
    print('RMSE:', round(RMSE(y_pred, y_true), precision))
    print('MAE:', round(MAE(y_pred, y_true), precision))
    print('MAPE:', round(MAPE(y_pred, y_true)*100, precision), '%')
    print('PCC:', round(PCC(y_pred, y_true), precision))
    return MSE(y_pred, y_true), RMSE(y_pred, y_true), MAE(y_pred, y_true), MAPE(y_pred, y_true)

def MSE(y_pred: np.array, y_true: np.array):
    return np.mean(np.square(y_pred - y_true))

def RMSE(y_pred:np.array, y_true:np.array):
    return np.sqrt(MSE(y_pred, y_true))

def MAE(y_pred:np.array, y_true:np.array):
    return np.mean(np.abs(y_pred - y_true))

def MAPE(y_pred:np.array, y_true:np.array, epsilon=1e-0):       # avoid zero division
    return np.mean(np.abs(y_pred - y_true) / (y_true + epsilon))

def PCC(y_pred:np.array, y_true:np.array):      # Pearson Correlation Coefficient
    return np.corrcoef(y_pred.flatten(), y_true.flatten())[0,1]


import numpy as np

def relaxation(A, b, omega, eps, maxIterations):
  size = len(A)
  x = np.zeros(size)

  for k in range(maxIterations):
    xPrev = np.copy(x)
    for j in range(size):
      d = b[j]

      for i in range(size):
        if j != i:
          d -= A[j][i] * x[i]
      x[j] = (1 - omega) * x[j] + (omega / A[j][j]) * d

    print(f"{k}-th iteration:")
    print(x)
    if np.linalg.norm(np.dot(A, x) - b) <= eps * np.linalg.norm(b):
      print("^^^^^^^^^^^^^^")
      print(">> Малость невязки << Solution found")
      return
    if np.linalg.norm(x - xPrev) <= eps * np.linalg.norm(xPrev):
      print("^^^^^^^^^^^^^^")
      print(">> Малость нормы приближений << Solution found")
      return

  print("Number of iterations exceeded")


import pandas as pd
import numpy as np
import tsplib95

def distance(a, b):
    """Euclidean distance of two points.

    Args:
        a (numpy.array or pd.Series)
        b (numpy.array or pd.Series)

    Returns:
        [float]
    """
    return np.sqrt(np.sum(np.power((a-b), 2)))

def get_dist_matrix(path):
    """Given a csv of rows each containing
    index, x, y coordinates, return the distance matrix,
    for the group of points.
    """
    df = pd.read_csv(path,
                     sep=' ',
                     names=['x', 'y'])

    full_matrix = []
    for i in df.index:
        vec_distance = []
        for j in df.index:
            vec_distance.append(distance(df.iloc[i,].to_numpy(),
                                    df.iloc[j,].to_numpy()))
        vec_distance = np.array(vec_distance)
        full_matrix.append(vec_distance)
    full_matrix = np.array(full_matrix)
    return full_matrix

def tsplib_distance_matrix(tsplib_file: str) -> np.ndarray:
    """Distance matrix from a TSPLIB file
    Parameters
    ----------
    tsplib_file
        A string with the complete path of the TSPLIB file (or just its name if
        it is the in current path)
    Returns
    -------
    distance_matrix
        A ND-array with the equivalent distance matrix of the input file
    Notes
    -----
    This function can handle any file supported by the `tsplib95` lib.
    """

    tsp_problem = tsplib95.load(tsplib_file)
    distance_matrix_flattened = np.array([
        tsp_problem.get_weight(*edge) for edge in tsp_problem.get_edges()
    ])
    distance_matrix = np.reshape(
        distance_matrix_flattened,
        (tsp_problem.dimension, tsp_problem.dimension),
    )

    # Some problems with EXPLICIT matrix have a large number in the distance
    # from a node to itself, which makes no sense for our problems. Thus,
    # always ensure a diagonal filled with zeros
    np.fill_diagonal(distance_matrix, 0)
    return distance_matrix


def SWHeart(u, v, alpha):
# SWHEART Calculate the heart of the Wilson function.
# SWHeart(u, v, alpha) calculates the matrix H (Heart of the Wilson
# function) for maturities specified by vectors u and v. The formula is
# taken from the EIOPA technical specifications paragraph 132.
#
# Arguments:
#    u =     n_1 x 1 vector of maturities. Ex. u = [1; 3]
#    v =     n_2 x 1 vector of maturities. Ex. v = [1; 2; 3; 5]
#    alpha = 1 x 1 floating number representing the convergence speed parameter alpha. Ex. alpha = 0.05
#
# Returns:
#    n_1 x n_2 matrix representing the Heart of the Wilson function for selected maturities and parameter alpha. H is calculated as in the paragraph 132 of the EIOPA documentation.
#
# For more information see https://www.eiopa.europa.eu/sites/default/files/risk_free_interest_rate/12092019-technical_documentation.pdf
    import numpy as np

    u_Mat = np.tile(u, [v.size, 1]).transpose()
    v_Mat = np.tile(v, [u.size, 1])
    return 0.5 * (alpha * (u_Mat + v_Mat) + np.exp(-alpha * (u_Mat + v_Mat)) - alpha * np.absolute(u_Mat-v_Mat) - np.exp(-alpha * np.absolute(u_Mat-v_Mat))); # Heart of the Wilson function from paragraph 132


# RESPUESTA 2
import numpy as np
from scipy import stats
from math import log

import plotly.plotly as py
import plotly.graph_objs as go

from funciones import gcl_uniforme
import constante

# Paso 1: Generamos muestras de la variable uniforme U
x_n = constante.SEMILLA
u = []  # array de uniformes
x = []  # array de inversas

for _ in range(constante.CANT_EXPERIMENTOS):
    x_n = gcl_uniforme(x_n)
    u.append(x_n)

#  Paso 2: Aplicar la transformacion inversa
paramLambda = float(1)/float(15)
for i in range(len(u)):
    x.append(-log(1-u[i])/paramLambda)  # Transformacion inversa

# Mostramos histograma del resultado
data = [go.Histogram(x=x)]
py.plot(data, filename='histograma-inversa-exponencial')

# Mostramos media, varianza y moda muestrales y teoricos
media = np.mean(x)
varianza = np.var(x)
moda = max(set(x), key=x.count)

print("Media muestral: {0} Varianza muestral: {1} Moda muestral: {2}".format(media, varianza, moda))
print("Media teorica:  {0} Varianza teorica:  {1} Moda teorica:  {2}".format(15, 15*15, 0))


''' principal_component_analysis.py
Principal Component Analysis reduces the dimensionality of the feature set by
sorting the features by the explained variance.  It does this by
1) computing a covariance matrix for the features
2) finding the eigenvectors and eigenvalues of the matrix, principal components.
3) computing explained variance for the components and sorting them

Always standardize because PCA is sensitive to scaling

Created on Jul 2, 2016

from Python Machine Learning by Sebastian Raschka under the following license

The MIT License (MIT)

Copyright (c) 2015, 2016 SEBASTIAN RASCHKA (mail@sebastianraschka.com)

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@author: richard lyman
'''
n_components = 10 # of of pca components to use for final accuracy

import numpy as np
import ocr_utils
import matplotlib.pyplot as plt
from sklearn.metrics import accuracy_score
from sklearn.linear_model import LogisticRegression
from sklearn.decomposition import PCA
from sklearn.decomposition import KernelPCA

chars_to_train = range(48,58)
columnsXY=range(0,20)
column_str = 'column_sum{}'.format(list(columnsXY))

input_filters_dict = {'m_label': chars_to_train, 'font': 'E13B'}

# output  the character label and the image and column sums
output_feature_list = ['m_label','image',column_str]

# read the complete image (20x20) = 400 pixels for each character
ds = ocr_utils.read_data(input_filters_dict=input_filters_dict,
                            output_feature_list=output_feature_list,
                            test_size=.2,
                            random_state=0)
windows_limit = 5000 # uses too much memory for my 32 bit windows computer so limit size of sample
y_train = ds.train.features[0][:windows_limit]
X_train_image = ds.train.features[1][:windows_limit]
X_train = ds.train.features[2][:windows_limit]

y_test = ds.test.features[0]
X_test_image = ds.test.features[1]
X_test = ds.test.features[2]

cov_mat = np.cov(X_train_image.T)
eigen_vals, eigen_vecs = np.linalg.eig(cov_mat)

print('\nEigenvalues \n%s' % eigen_vals[:2*n_components])

tot = sum(eigen_vals)
var_exp = [(i / tot) for i in sorted(eigen_vals, reverse=True)]
cum_var_exp = np.cumsum(var_exp)
var_exp = var_exp[:20]
cum_var_exp = cum_var_exp[:2*n_components]
title='explained variance'
plt.bar(range(1, len(var_exp)+1), var_exp, alpha=0.5, align='center',   label='individual explained variance')
plt.step(range(1, len(cum_var_exp)+1), cum_var_exp, where='mid',
         label='cumulative explained variance')
plt.ylabel('Explained variance ratio')
plt.xlabel('Principal components')
plt.legend(loc='best')
plt.tight_layout()
plt.title(title)
ocr_utils.show_figures(plt,title)

# Make a list of (eigenvalue, eigenvector) tuples
eigen_pairs = [(np.abs(eigen_vals[i]), eigen_vecs[:,i]) for i in range(len(eigen_vals))]

# Sort the (eigenvalue, eigenvector) tuples from high to low
eigen_pairs.sort(reverse=True)

# The eigenpairs with the highest explained variance
w = np.hstack((eigen_pairs[0][1][:, np.newaxis],
               eigen_pairs[1][1][:, np.newaxis]))
print('Matrix W:\n', w[:2*n_components,:])

X_train_pca = X_train_image.dot(w)
print ('projection of first dataset sample on first 2 eignvectors {}'.format(X_train_image[0].dot(w)))

markers = ('o', 'v', '^', '<', '>', '8', 's', 'p', '*', 'h', 'H', 'D', 'd')
colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan','orange','green','brown','lightblue','pink')

for l, c, m in zip(np.unique(y_train), colors, markers):
    plt.scatter(X_train_pca[y_train==l, 0],
                X_train_pca[y_train==l, 1],
                c=c, label=l, marker=m)

plt.xlabel('PC 1')
plt.ylabel('PC 2')
plt.legend(loc='lower left')
plt.tight_layout()
title='features mapped to two principal components'
plt.title(title)
ocr_utils.show_figures(plt,title)

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

pca = PCA(n_components=2)

X_train_pca = pca.fit_transform(X_train_image)
X_test_pca = pca.transform(X_test_image)

lr = LogisticRegression(solver='liblinear',multi_class='auto')
logistic_fitted =lr.fit(X_train_pca, y_train)

print('\nPCA Train Accuracy: {:4.6f}, n_components={}'.format(accuracy_score(y_train, logistic_fitted.predict(X_train_pca)),pca.n_components))
print('PCA Test Accuracy: {:4.6f}, n_components={}'.format(accuracy_score(y_test, logistic_fitted.predict(X_test_pca)),pca.n_components))

title = 'train pc1 versus pc2'
ocr_utils.plot_decision_regions(X=X_train_pca, y=y_train, classifier=lr, labels=['pc1','pc2'], title=title)

title = 'test pc1 versus pc2'
ocr_utils.plot_decision_regions(X=X_test_pca, y=y_test, classifier=lr, labels=['pc1','pc2'], title=title)
X_train_pca = pca.fit_transform(X_train_image)
X_test_pca = pca.transform(X_test_image)

########################################################################################
pca = PCA(n_components=n_components)
X_train_pca = pca.fit_transform(X_train_image)
X_test_pca = pca.transform(X_test_image)

lr = LogisticRegression(solver='liblinear',multi_class='auto')
logistic_fitted = lr.fit(X_train_pca, y_train)

y_train_pred = logistic_fitted.predict(X_train_pca)
y_test_pred = logistic_fitted.predict(X_test_pca)

print('\nPCA Train Accuracy: {:4.6f}, n_components={}'.format(accuracy_score(y_train, y_train_pred),pca.n_components))
print('PCA Test Accuracy: {:4.6f}, n_components={}'.format(accuracy_score(y_test, y_test_pred),pca.n_components))

X_errors_image = X_test_image[y_test!=y_test_pred]
y_errors = y_test[y_test!=y_test_pred]
X_errors_pca = X_test_pca[y_test!=y_test_pred]

X_orig = X_train_image[:500]
title = 'originals'
X2D=np.reshape(X_orig, (X_orig.shape[0], ds.train.num_rows, ds.train.num_columns))
ocr_utils.montage(X2D,title=title)

X_orig = X_train_pca[:500]
title = 'inverse original'
X_inverse = pca.inverse_transform(X_orig)
X2D = np.reshape(X_inverse, (X_inverse.shape[0], ds.train.num_rows, ds.train.num_columns))
X2D = X2D - np.min(X2D)
ocr_utils.montage(X2D,title=title)

# change to a 2D shape
X_errors2D=np.reshape(X_errors_image, (X_errors_image.shape[0], ds.train.num_rows, ds.train.num_columns))
ocr_utils.montage(X_errors2D,title='PCA Error Characters, components={}'.format (n_components))

title = 'inverse transform errors'
X_inverse = pca.inverse_transform(X_errors_pca)
X2D=np.reshape(X_inverse, (X_inverse.shape[0], ds.train.num_rows, ds.train.num_columns))
X2D = X2D - np.min(X2D)
ocr_utils.montage(X2D,title=title)

########################################################################################
kernel='rbf'  # really slow
pca = KernelPCA(n_components=2,kernel=kernel, gamma=15)

X_train_pca = pca.fit_transform(X_train_image)
X_test_pca = pca.transform(X_test_image)

lr = LogisticRegression(solver='liblinear',multi_class='auto')
logistic_fitted=lr.fit(X_train_pca, y_train)
y_train_pred = logistic_fitted.predict(X_train_pca)
y_test_pred = logistic_fitted.predict(X_test_pca)

print('\nKernel PCA Train Accuracy: {:4.6f}, n_components={}, kernel={}'.format(accuracy_score(y_train, y_train_pred), pca.n_components,kernel))
print('Kernel PCA Test Accuracy: {:4.6f}, n_components={}, kernel={}'.format(accuracy_score(y_test, y_test_pred),pca.n_components,kernel))

title = 'train kernel {} pc1 versus pc2'.format(kernel)
ocr_utils.plot_decision_regions(X=X_train_pca, y=y_train, classifier=lr, labels=['pc1','pc2'], title=title)

title = 'test kernel {} pc1 versus pc2'.format(kernel)
ocr_utils.plot_decision_regions(X=X_test_pca, y=y_test, classifier=lr, labels=['pc1','pc2'], title=title)

########################################################################################
# too slow on my computer

# pca = KernelPCA(n_components=n_components,kernel=kernel, gamma = 15)
#
# X_train_pca = pca.fit_transform(X_train_image)
# X_test_pca = pca.transform(X_test_image)
#
# print ('n_components={}'.format(pca.n_components))
#
# lr = LogisticRegression()
# logistic_fitted = lr.fit(X_train_pca, y_train)
#
# y_pred = logistic_fitted.predict(X_test_pca)
# print('\nKKernelPCA Train Accuracy: {:4.6f}, n_components={}, kernel={}'.format(accuracy_score(y_train, logistic_fitted.predict(X_train_pca)), pca.n_components, kernel))
# print('KernelPCA Test Accuracy: {:4.6f}, n_components={}, kernel={}'.format(accuracy_score(y_test, y_pred), pca.n_components, kernel))
#
# X_errors_image = X_test_image[y_test!=y_pred]
# y_errors = y_test[y_test!=y_pred]
#
# error_images = X_errors_image.shape[0]
#
# # change to a 2D shape
# X_errors2D=np.reshape(X_errors_image, (error_images, ds.train.num_rows, ds.train.num_columns))
# ocr_utils.montage(X_errors2D,title='Kernel {} KernelPCA Errors Character,components={}s'.format(kernel,n_components))

print ('\n########################### No Errors ####################################')


from sympy import *
from tait_bryan_R_utils import *
from rodrigues_R_utils import *
from quaternion_R_utils import *
from plucker_line_utils import *

fx,fy,cx,cy = symbols('fx fy cx cy');
px, py, pz = symbols('px py pz')
om, fi, ka = symbols('om fi ka')
#sx, sy, sz = symbols('sx sy sz')
#q0, q1, q2, q3 = symbols('q0 q1 q2 q3')
mx_w, my_w, mz_w, lx_w, ly_w, lz_w = symbols('mx_w my_w mz_w lx_w ly_w lz_w')
u_s,v_s = symbols('u_s v_s')
u_e,v_e = symbols('u_e v_e')

position_symbols = [px, py, pz]
orientation_symbols = [om, fi, ka]
#orientation_symbols = [sx, sy, sz]
#orientation_symbols = [q0, q1, q2, q3]
plucker_line_symbols = [mx_w, my_w, mz_w]
all_symbols = position_symbols + orientation_symbols + plucker_line_symbols

RT_wc = matrix44FromTaitBryan(px, py, pz, om, fi, ka)
#RT_wc = matrix44FromRodrigues(px, py, pz, sx, sy, sz)
#RT_wc = matrix44FromQuaternion(px, py, pz, q0, q1, q2, q3)
K=plucker_line_K(fx, fy, cx, cy)
mm_cw = plucker_line_motion_matrix_cw(RT_wc)
l_w = Matrix([[mx_w],[my_w],[mz_w],[lx_w],[ly_w],[lz_w]])
l_c = mm_cw*l_w
m_c=Matrix([[l_c[0,0]],[l_c[1,0]],[l_c[2,0]]])
l=K*m_c

x_s=Matrix([[u_s,v_s,1]])
x_e=Matrix([[u_e,v_e,1]])

target_value = Matrix([0,0]).vec()
model_function = Matrix([(x_s*l)/sqrt(l[0,0]*l[0,0] + l[1,0]*l[1,0]),(x_e*l)/sqrt(l[0,0]*l[0,0] + l[1,0]*l[1,0])]).vec()
obs_eq = target_value - model_function
obs_eq_jacobian = obs_eq.jacobian(all_symbols)

print(obs_eq)
print(obs_eq_jacobian)

with open("perspective_camera_plucker_line_tait_bryan_wc_jacobian.h",'w') as f_cpp:
    f_cpp.write("inline void observation_equation_perspective_camera_plucker_line_tait_bryan_wc(Eigen::Matrix<double, 2, 1> &delta, double fx, double fy, double cx, double cy, double px, double py, double pz, double om, double fi, double ka, double mx_w, double my_w, double mz_w, double lx_w, double ly_w, double lz_w, double u_s, double v_s, double u_e, double v_e)\n")
    f_cpp.write("{")
    f_cpp.write("delta.coeffRef(0,0) = %s;\n"%(ccode(obs_eq[0,0])))
    f_cpp.write("delta.coeffRef(1,0) = %s;\n"%(ccode(obs_eq[1,0])))
    f_cpp.write("}")
    f_cpp.write("\n")
    f_cpp.write("inline void observation_equation_perspective_camera_plucker_line_tait_bryan_wc_jacobian(Eigen::Matrix<double, 2, 6, Eigen::RowMajor> &j, double fx, double fy, double cx, double cy, double px, double py, double pz, double om, double fi, double ka, double mx_w, double my_w, double mz_w, double lx_w, double ly_w, double lz_w, double u_s, double v_s, double u_e, double v_e)\n")
    f_cpp.write("{")
    for i in range (2):
        for j in range (6):
            f_cpp.write("j.coeffRef(%d,%d) = %s;\n"%(i,j, ccode(obs_eq_jacobian[i,j])))
    f_cpp.write("}")


from __future__ import division
from past.utils import old_div
import numpy as np

def angular_distance_fast(ra1, dec1, ra2, dec2):
    """
    Compute angular distance using the Haversine formula. Use this one when you know you will never ask for points at
    their antipodes. If this is not the case, use the angular_distance function which is slower, but works also for
    antipodes.

    :param lon1:
    :param lat1:
    :param lon2:
    :param lat2:
    :return:
    """

    lon1 = np.deg2rad(ra1)
    lat1 = np.deg2rad(dec1)
    lon2 = np.deg2rad(ra2)
    lat2 = np.deg2rad(dec2)
    dlon = lon2 - lon1
    dlat = lat2 - lat1

    a = np.sin(old_div(dlat,2.0))**2 + np.cos(lat1) * np.cos(lat2) * np.sin(old_div(dlon,2.0))**2
    c = 2 * np.arcsin(np.sqrt(a))
    return np.rad2deg(c)

def angular_distance(ra1, dec1, ra2, dec2):
    """
    Returns the angular distance between two points, two sets of points, or a set of points and one point.

    :param ra1: array or float, longitude of first point(s)
    :param dec1: array or float, latitude of first point(s)
    :param ra2: array or float, longitude of second point(s)
    :param dec2: array or float, latitude of second point(s)
    :return: angular distance(s) in degrees
    """

    # Vincenty formula, slower than the Haversine formula in some cases, but stable also at antipodes

    lon1 = np.deg2rad(ra1)
    lat1 = np.deg2rad(dec1)
    lon2 = np.deg2rad(ra2)
    lat2 = np.deg2rad(dec2)

    sdlon = np.sin(lon2 - lon1)
    cdlon = np.cos(lon2 - lon1)
    slat1 = np.sin(lat1)
    slat2 = np.sin(lat2)
    clat1 = np.cos(lat1)
    clat2 = np.cos(lat2)

    num1 = clat2 * sdlon
    num2 = clat1 * slat2 - slat1 * clat2 * cdlon
    denominator = slat1 * slat2 + clat1 * clat2 * cdlon

    return np.rad2deg(np.arctan2(np.sqrt(num1 ** 2 + num2 ** 2), denominator))

def spherical_angle( ra0, dec0, ra1, dec1, ra2, dec2 ):
    """
    Returns the spherical angle distance between two sets of great circles defined by (ra0, dec0), (ra1, dec1) and (ra0, dec0), (ra2, dec2)

    :param ra0: array or float, longitude of intersection point(s)
    :param dec0: array or float, latitude of intersection point(s)
    :param ra1: array or float, longitude of first point(s)
    :param dec1: array or float, latitude of first point(s)
    :param ra2: array or float, longitude of second point(s)
    :param dec2: array or float, latitude of second point(s)
    :return: spherical angle in degrees
    """

    a = np.deg2rad( angular_distance(ra0, dec0, ra1, dec1))
    b = np.deg2rad( angular_distance(ra0, dec0, ra2, dec2))
    c = np.deg2rad( angular_distance(ra2, dec2, ra1, dec1))

    #use the spherical law of cosines: https://en.wikipedia.org/wiki/Spherical_law_of_cosines#Rearrangements

    numerator = np.atleast_1d( np.cos(c) - np.cos(a) * np.cos(b) )
    denominator = np.atleast_1d( np.sin(a)*np.sin(b) )

    return np.where( denominator == 0 , np.zeros( len(denominator)), np.rad2deg( np.arccos( old_div(numerator,denominator))) )


"""
Linear Regression with TensorFlow
sungjoon.choi@cpslab.snu.ac.kr
"""
import tensorflow as tf
import numpy as np
import matplotlib.pyplot as plt

# Generate training data
np.random.seed(1)
def f(x,a,b):
    n    = train_X.size
    vals = np.zeros((1, n))
    for i in range(0, n):
        ax  = np.multiply(a, x.item(i))
        val = np.add(ax, b)
        vals[0, i] = val
    return vals

Wref = 0.7
bref = -1.
n    = 20
noise_var = 0.001
train_X   = np.random.random((1, n))
ref_Y     = f(train_X, Wref, bref)
train_Y   = ref_Y + np.sqrt(noise_var)*np.random.randn(1, n)
n_samples = train_X.size # <= Just for using size operator
print ("")
print (" Type of 'train_X' is ", type(train_X))
print (" Shape of 'train_X' is %s" % (train_X.shape,))
print (" Type of 'train_Y' is ", type(train_Y))
print (" Shape of 'train_Y' is %s" % (train_Y.shape,))

# Plot
# plt.figure(1)
# plt.plot(train_X[0, :], ref_Y[0, :], 'ro', label='Original data')
# plt.plot(train_X[0, :], train_Y[0, :], 'bo', label='Training data')
# plt.axis('equal')
# plt.legend(loc='lower right')

# Prepare for Linear Regression

# Parameters
training_epochs = 2000
display_step    = 50

# Set TensorFlow Graph
X = tf.placeholder(tf.float32, name="input")
Y = tf.placeholder(tf.float32, name="output")
W = tf.Variable(np.random.randn(), name="weight")
b = tf.Variable(np.random.randn(), name="bias")

# Construct a Model
activation = tf.add(tf.multiply(X, W), b)

# Define Error Measure and Optimizer
learning_rate   = 0.01
cost = tf.reduce_mean(tf.pow(activation-Y, 2))
# learning_rate   = 0.001
# cost = tf.sqrt(tf.reduce_sum(tf.pow(activation-Y, 2)))
optimizer = tf.train.GradientDescentOptimizer(learning_rate).minimize(cost) #Gradient descent

"""
 tf.reduce_sum()
 tf.reduce_mean()
 _____

 tf.pow(Yhat, Y, 2)
 tf.nn.softmax_cross_entropy_with_logits(Yhat, Y)
 _____

 tf.train.GradientDescentOptimizer(0.05).minimize(cost)
 tf.train.RMSPropOptimizer(0.001, 0.9).minimize(cost)
"""

# Initializer
init = tf.global_variables_initializer()

# Run!
sess = tf.Session()

# Initialize
sess.run(init)
for epoch in range(training_epochs):
    for (x, y) in zip(train_X[0, :], train_Y[0, :]):
        # print "x: ", x, " y: ", y
        sess.run(optimizer, feed_dict={X:x, Y:y})

    # Check cost
    if epoch % display_step == 0:
        costval = sess.run(cost, feed_dict={X: train_X, Y:train_Y})
        print ("Epoch:", "%04d"%(epoch+1), "cost=", "{:.5f}".format(costval))
        Wtemp = sess.run(W)
        btemp = sess.run(b)
        print (" Wtemp is", "{:.4f}".format(Wtemp), "btemp is", "{:.4f}".format(btemp))
        print (" Wref is", "{:.4f}".format(Wref), "bref is", "{:.4f}".format(bref))

# Final W and b
Wopt = sess.run(W)
bopt = sess.run(b)
fopt = f(train_X, Wopt, bopt)

# Plot Results
plt.figure(2)
plt.plot(train_X[0, :], ref_Y[0, :], 'ro', label='Original data')
plt.plot(train_X[0, :], train_Y[0, :], 'bo', label='Training data')
plt.plot(train_X[0, :], fopt[0, :], 'k-', label='Fitted Line')
plt.axis('equal')
plt.legend(loc='lower right')
plt.show()


__author__ = 'jhaux'

import cv2
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
import os

import image_operations as imop
import ltm_analysis as ltm
import jimlib as jim

from numpy import linspace , arange , reshape ,zeros
from scipy.fftpack import fft2 , fftfreq
from cmath import pi

# create some arbitrary data
some_data = arange(0.0 , 16384.0 , dtype = complex)

# reshape it to be a 128x128 2d grid
some_data_grid = reshape(some_data , (128 , 128) )

# assign some real spatial co-ordinates to the grid points
# first define the edge values
x_min = -250.0
x_max = 250.0
y_min = -250.0
y_max = 250

# then create some empty 2d arrays to hold the individual cell values
x_array = zeros( (128,128) , dtype = float )
y_array = zeros( (128,128) , dtype = float )

# now fill the arrays with the associated values
for row , y_value in enumerate(linspace (y_min , y_max , num = 128) ):

  for column , x_value in enumerate(linspace (x_min , x_max , num = 128) ):

    x_array[row][column] = x_value
    y_array[row][column] = y_value

# now for any row,column pair the x_array and y_array hold the spatial domain
# co-ordinates of the associated point in some_data_grid

# now use the fft to transform the data to the wavenumber domain
some_data_wavedomain = fft2(some_data_grid)

# now we can use fftfreq to give us a base for the wavenumber co-ords
# this returns [0.0 , 1.0 , 2.0 , ... , 62.0 , 63.0 , -64.0 , -63.0 , ... , -2.0 , -1.0 ]
n_value = fftfreq( 128 , (1.0 / 128.0 ) )

# now we can initialize some arrays to hold the wavenumber co-ordinates of each cell
kx_array = zeros( (128,128) , dtype = float )
ky_array = zeros( (128,128) , dtype = float )

# before we can calculate the wavenumbers we need to know the total length of the spatial
# domain data in x and y. This assumes that the spatial domain units are metres and
# will result in wavenumber domain units of radians / metre.
x_length = x_max - x_min
y_length = y_max - y_min

# now the loops to calculate the wavenumbers
for row in xrange(128):

  for column in xrange(128):

    kx_array[row][column] = ( 2.0 * pi * n_value[column] ) / x_length
    ky_array[row][column] = ( 2.0 * pi * n_value[row] ) / y_length

# now for any row,column pair kx_array , and ky_array will hold the wavedomain coordinates
# of the correspoing point in some_data_wavedomain


import numpy as np

def get_coeffs(order: int):
    # Coefficients of order r=2
    # On smooth solutions this should converge with order r=3
    Cs = []
    As = []
    Sigs = []

    Cs.append(np.array([1, 2]) / 3)
    As.append(
        np.array(
            [
                [3, -1],
                [1, 1],
            ]
        )
        / 2
    )
    Sigs.append(np.array([[[1, 0], [-2, 1]], [[1, 0], [-2, 1]]], dtype=np.float32))

    # Coefficients of order r=3
    # On smooth solutions this should converge with order r=5
    Cs.append(np.array([1, 6, 3]) / 10)
    As.append(
        np.array(
            [
                [11, -7, 2],
                [2, 5, -1],
                [-1, 5, 2],
            ]
        )
        / 6
    )
    const = 3.0
    s3 = (
        np.array(
            [
                [[10, 0, 0], [-31, 25, 0], [11, -19, 4]],
                [[4, 0, 0], [-13, 13, 0], [5, -13, 4]],
                [[4, 0, 0], [-19, 25, 0], [11, -31, 10]],
            ],
            dtype=np.float32,
        )
        / 3.0
    )
    s3 = s3.astype(np.float32)
    Sigs.append(s3)

    C = Cs[order - 2]
    a = As[order - 2]
    sigma = Sigs[order - 2]

    return C, a, sigma

def reconstruct(order, q):
    """
    Do WENO reconstruction

    Parameters
    ----------

    order : int
        The stencil width
    q : numpy array
        Scalar data to reconstruct

    Returns
    -------

    qL : numpy array
        Reconstructed data - boundary points are zero
    """

    C, a, sigma = get_coeffs(order)

    qL = np.zeros_like(q)
    beta = np.zeros((order, len(q)))
    w = np.zeros_like(order)
    npt = len(q) - 2 * order
    epsilon = 1e-16
    for i in range(order, npt + order):
        q_stencils = np.zeros(order)
        alpha = np.zeros(order)
        for k in range(order):
            for l in range(order):
                for m in range(l + 1):
                    beta[k, i] += sigma[k, l, m] * q[i + k - l] * q[i + k - m]
            alpha[k] = C[k] / (epsilon + beta[k, i] ** 2)
            for l in range(order):
                q_stencils[k] += a[k, l] * q[i + k - l]
        w = alpha / np.sum(alpha)
        qL[i] = np.dot(w, q_stencils)

    return qL


import scipy.linalg as linalg
import numpy as np
import contextlib

A = np.zeros((10,10))
for i in range(10):
    for j in range(10):
        if (i==j) :
            A[i][j] = 1
        if (abs(i-j) == 1):
            A[i][j] = 0.2

inv_A = np.linalg.inv(A)
print("inv_A=")
np.set_printoptions(formatter={'float': '{: 0.3f}'.format})
print(inv_A)

P,L,U = linalg.lu(A)
print("L=")
print(L)
print("U=")
print(U)


import numpy as np

def wrapToPi(angle):
    return (angle + np.pi) % (2*np.pi) - np.pi

def rotmat2d(angle):
    return np.array([[np.cos(angle), -np.sin(angle)],
                     [np.sin(angle),  np.cos(angle)]])


import os
import sys
import cv2
import numpy as np

def xyz2lonlat(xyz):
    atan2 = np.arctan2
    asin = np.arcsin

    norm = np.linalg.norm(xyz, axis=-1, keepdims=True)
    xyz_norm = xyz / norm
    x = xyz_norm[..., 0:1]
    y = xyz_norm[..., 1:2]
    z = xyz_norm[..., 2:]

    lon = atan2(x, z)
    lat = asin(y)
    lst = [lon, lat]

    out = np.concatenate(lst, axis=-1)
    return out

def lonlat2XY(lonlat, shape):
    X = (lonlat[..., 0:1] / (2 * np.pi) + 0.5) * (shape[1] - 1)
    Y = (lonlat[..., 1:] / (np.pi) + 0.5) * (shape[0] - 1)
    lst = [X, Y]
    out = np.concatenate(lst, axis=-1)

    return out

class Equirectangular:
    def __init__(self, img_name):
        self._img = cv2.imread(img_name, cv2.IMREAD_COLOR)
        [self._height, self._width, _] = self._img.shape
        #cp = self._img.copy()
        #w = self._width
        #self._img[:, :w/8, :] = cp[:, 7*w/8:, :]
        #self._img[:, w/8:, :] = cp[:, :7*w/8, :]

    def GetPerspective(self, FOV, THETA, PHI, height, width):
        #
        # THETA is left/right angle, PHI is up/down angle, both in degree
        #

        f = 0.5 * width * 1 / np.tan(0.5 * FOV / 180.0 * np.pi)
        cx = (width - 1) / 2.0
        cy = (height - 1) / 2.0
        K = np.array([
                [f, 0, cx],
                [0, f, cy],
                [0, 0,  1],
            ], np.float32)
        K_inv = np.linalg.inv(K)

        x = np.arange(width)
        y = np.arange(height)
        x, y = np.meshgrid(x, y)
        z = np.ones_like(x)
        xyz = np.concatenate([x[..., None], y[..., None], z[..., None]], axis=-1)
        xyz = xyz @ K_inv.T

        y_axis = np.array([0.0, 1.0, 0.0], np.float32)
        x_axis = np.array([1.0, 0.0, 0.0], np.float32)
        R1, _ = cv2.Rodrigues(y_axis * np.radians(THETA))
        R2, _ = cv2.Rodrigues(np.dot(R1, x_axis) * np.radians(PHI))
        R = R2 @ R1
        xyz = xyz @ R.T
        lonlat = xyz2lonlat(xyz)
        XY = lonlat2XY(lonlat, shape=self._img.shape).astype(np.float32)
        persp = cv2.remap(self._img, XY[..., 0], XY[..., 1], cv2.INTER_CUBIC, borderMode=cv2.BORDER_WRAP)

        return persp


from numpy.linalg import eig, inv, svd
from math import atan2
import numpy as np

def __fit_ellipse(x, y):
    x, y = x[:, np.newaxis], y[:, np.newaxis]
    D = np.hstack((x * x, x * y, y * y, x, y, np.ones_like(x)))
    S, C = np.dot(D.T, D), np.zeros([6, 6])
    C[0, 2], C[2, 0], C[1, 1] = 2, 2, -1
    U, s, V = svd(np.dot(inv(S), C))
    a = U[:, 0]
    return a

def ellipse_center(a):
    b, c, d, f, g, a = a[1] / 2, a[2], a[3] / 2, a[4] / 2, a[5], a[0]
    num = b * b - a * c
    x0 = (c * d - b * f) / num
    y0 = (a * f - b * d) / num
    return np.array([x0, y0])

def ellipse_axis_length(a):
    b, c, d, f, g, a = a[1] / 2, a[2], a[3] / 2, a[4] / 2, a[5], a[0]
    up = 2 * (a * f * f + c * d * d + g * b * b - 2 * b * d * f - a * c * g)
    down1 = (b * b - a * c) * (
        (c - a) * np.sqrt(1 + 4 * b * b / ((a - c) * (a - c))) - (c + a)
    )
    down2 = (b * b - a * c) * (
        (a - c) * np.sqrt(1 + 4 * b * b / ((a - c) * (a - c))) - (c + a)
    )
    res1 = np.sqrt(up / down1)
    res2 = np.sqrt(up / down2)
    return np.array([res1, res2])

def ellipse_angle_of_rotation(a):
    b, c, d, f, g, a = a[1] / 2, a[2], a[3] / 2, a[4] / 2, a[5], a[0]
    return atan2(2 * b, (a - c)) / 2

def fit_ellipse(x, y):
    """@brief fit an ellipse to supplied data points: the 5 params
        returned are:

        M - major axis length
        m - minor axis length
        cx - ellipse centre (x coord.)
        cy - ellipse centre (y coord.)
        phi - rotation angle of ellipse bounding box

    @param x first coordinate of points to fit (array)
    @param y second coord. of points to fit (array)
    """
    a = __fit_ellipse(x, y)
    centre = ellipse_center(a)
    phi = ellipse_angle_of_rotation(a)
    M, m = ellipse_axis_length(a)
    # assert that the major axix M > minor axis m
    if m > M:
        M, m = m, M
    # ensure the angle is betwen 0 and 2*pi
    phi -= 2 * np.pi * int(phi / (2 * np.pi))
    return [M, m, centre[0], centre[1], phi]


## module lamRange
''' r = lamRange(d,c,N).
    Returns the sequence {r[0],r[1],...,r[N]} that
    separates the N lowest eigenvalues of the tridiagonal
    matrix; that is, r[i] < lam[i] < r[i+1].
'''
import numpy as np
from sturmSeq import *
from gerschgorin import *

def lamRange(d,c,N):
    lamMin,lamMax = gerschgorin(d,c)
    r = np.ones(N+1)
    r[0] = lamMin
  # Search for eigenvalues in descending order
    for k in range(N,0,-1):
      # First bisection of interval(lamMin,lamMax)
        lam = (lamMax + lamMin)/2.0
        h = (lamMax - lamMin)/2.0
        for i in range(1000):
          # Find number of eigenvalues less than lam
            p = sturmSeq(d,c,lam)
            numLam = numLambdas(p)
          # Bisect again & find the half containing lam
            h = h/2.0
            if numLam < k: lam = lam + h
            elif numLam > k: lam = lam - h
            else: break
      # If eigenvalue located, change the upper limit
      # of search and record it in [r]
        lamMax = lam
        r[k] = lam
    return r


import numpy as np

def trimf (x, params):

    if not isinstance(params, list):
        raise TypeError ("Params should be a list with three values.")
    if len(params) != 3:
        raise TypeError ("Number of parameters are incorrect. Expected 3 values in the list.")
    if not (isinstance(params[0], float) and isinstance(params[1], float) and isinstance(params[2], float)):
        raise TypeError ("Parameters should be in float.")
    if not(params[0] <= params[1]):
        raise TypeError("First value in list should be smaller than second one.")
    if not (params[1] <= params[2]):
        raise TypeError("Second value in list should be smaller than third one.")

    a = params[0]
    b = params[1]
    c = params[2]

    result = 0.0

    if x <= a:
        result = 0.0

    elif a <= x <= b:
        result = (x-a)/(b-a)

    elif b <= x <= c:
        result = (c-x)/(c-b)

    elif c <= x:
        result = 0

    return result


'''

do some calculations on a open channel flow inside an inclined pipe (like injector A11, Peregrino)

for now, only pipes (TYPE_PIPE) are supported.

the angle theta is like this (the pointed angle between dotted lines):

           %%%    %%%
      %%%              %%%

  %%%                      %%%

 %%%                         %%%
               .
 %%%          . .            %%%
             .   .
 %%%        .     .         %%%
           .       .
    %%%   .         .    %%%
         .           .
          %%%     %%%
'''

from scipy.optimize import fsolve
import pylab as pl

TYPE_PIPE = 'pipe'

def _flowarea(theta, r):
    return r**2/2. * (theta-pl.sin(theta))

def afa(theta, r):
    '''
    this is A*f(A)
    '''
    A = _flowarea(theta, r)
    return A**(5/3.) / (r*theta)**(2/3.)

def _manning_vmean(rh, n, s, k=1.):
    '''
    https://en.wikipedia.org/wiki/Manning_formula
    rh: hydraulic radius
    n: manning-roughness
    s: slope
    k: conversion factor. 1 for Si
    return: mean velocity [m/s]
    '''
    return k*rh**(2/3.)*pl.sqrt(s) / n

def _manning_func(q, n, s, k=1.):
    '''
    https://en.wikipedia.org/wiki/Manning_formula, after some manipulation
    q: flowrate
    n: manning-roughness
    s: slope
    k: conversion factor. 1 for Si
    return: value
    '''
    return q*n / (k*pl.sqrt(s))

def _hydr_rad(theta, r):
    p = r*theta      # wetted perimeter
    return _flowarea(theta, r)/p

def flow_characteristics(gtype, geom, inclin, q, n=0.022, k=1., full_output=False):
    '''
    given channel type, channel goemetry, flow-rate and inclination, it will
    calculate mean flow velocity and some geometric characteristics for the flow.
    # input
      gtype  : geometry type, f.ex. TYPE_PIPE
      geom   : struct with geometry details
      for TYPE_PIPE:
          geom.diam
      inclin : inclination of channel [deg]. 0 means horizontal
      q      : flowrate [m3/s]
      n      : manning friction factor. default is corrugated steel
      k      : unit conversion. 1. for Si
      full_output: use this one to get some info on the numerical solution (converged or not?)
    # output
      v      : mean velocity
      for TYPE_PIPE:
          theta  : angle discribing fluid channel (see above)
          w      : width of fluid channel (upper)
          h      : height of fluid channel
      ans : if full_output, this will be the full output from fsolve
    '''
    #
    s = pl.sin(inclin*pl.pi/180.)            # slope
    #
    if gtype == TYPE_PIPE:
        r = geom.diam/2.
        func = lambda theta: _flowarea(theta, r)*_manning_vmean(_hydr_rad(theta, r), n, s, k) - q
        theta0 = pl.pi/4.              # initial guess (0 got stuck)
        ans = fsolve(func, theta0, full_output=full_output)
        theta = ans[0][0] if full_output else ans[0]
        v = _manning_vmean(_hydr_rad(theta, r), n, s, k)
        h = r - r*pl.cos(theta/2.)
        w = 2*r*pl.sin(theta/2.)
        if full_output:
            return v, theta, w, h, ans   # angle, width, height, output from fsolve
        else:
            return v, theta, w, h        # angle, width, height


import numpy as np

def r2_score(y_true, y_pred):
    corr_matrix = np.corrcoef(y_true, y_pred)
    corr = corr_matrix[0, 1]
    return corr ** 2

class LinearRegression:
    def __init__(self, learning_rate=0.001, n_iters=1000):
        self.lr = learning_rate
        self.n_iters = n_iters
        self.weights = None
        self.bias = None

    def fit(self, X, y):
        n_samples, n_features = X.shape

        # init parameters
        self.weights = np.zeros(n_features)
        self.bias = 0

        # gradient descent
        for _ in range(self.n_iters):
            y_predicted = np.dot(X, self.weights) + self.bias
            # compute gradients
            dw = (1 / n_samples) * np.dot(X.T, (y_predicted - y))
            db = (1 / n_samples) * np.sum(y_predicted - y)

            # update parameters
            self.weights -= self.lr * dw
            self.bias -= self.lr * db

    def predict(self, X):
        y_approximated = np.dot(X, self.weights) + self.bias
        return y_approximated

# Testing
if __name__ == "__main__":
    # Imports
    import matplotlib.pyplot as plt
    from sklearn.model_selection import train_test_split
    from sklearn import datasets

    def mean_squared_error(y_true, y_pred):
        return np.mean((y_true - y_pred) ** 2)

    X, y = datasets.make_regression(
        n_samples=100, n_features=1, noise=20, random_state=4
    )

    X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=1234
    )

    regressor = LinearRegression(learning_rate=0.01, n_iters=1000)
    regressor.fit(X_train, y_train)
    predictions = regressor.predict(X_test)

    mse = mean_squared_error(y_test, predictions)
    print("MSE:", mse)

    accu = r2_score(y_test, predictions)
    print("Accuracy:", accu)

    y_pred_line = regressor.predict(X)
    cmap = plt.get_cmap("viridis")
    fig = plt.figure(figsize=(8, 6))
    m1 = plt.scatter(X_train, y_train, color=cmap(0.9), s=10)
    m2 = plt.scatter(X_test, y_test, color=cmap(0.5), s=10)
    plt.plot(X, y_pred_line, color="black", linewidth=2, label="Prediction")
    plt.show()


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

# Function to minmize
def f(x):
	return 8 * (x-10) * np.sin(0.5*x-5) + 200

# Derivative of the function
def fd(x):
	return 4 * (x - 10) * np.cos(0.5*x - 5) + 8 * np.sin(0.5*x-5)

x_min = -30
x_max = 30
x = np.linspace(x_min, x_max, 200)
y = f(x)
r = 0.005  # Learning rate
x_est = 25  # Starting point
y_est = f(x_est)

def animate(i):
	global x_est
	global y_est

	# Gradient descent
	x_est = x_est - fd(x_est) * r
	y_est = f(x_est)

	# Update the plot
	scat.set_offsets([[x_est,y_est]])
	text.set_text("Value : %.2f" % y_est)
	line.set_data(x, y)
	return line, scat, text

def init():
	line.set_data([], [])
	return line,

# Visualization Stuff
fig, ax = plt.subplots()
ax.set_xlim([x_min, x_max])
ax.set_ylim([-5, 500])
ax.set_xlabel("x")
ax.set_ylabel("f(x)")
plt.title("Gradient Descent Local Minima")
line, = ax.plot([], [])
scat = ax.scatter([], [], c="red")
text = ax.text(-25,450,"")

ani = animation.FuncAnimation(fig, animate, 40,
	init_func=init, interval=100, blit=True)

plt.show()


from math import pi, e, sqrt
import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import quad
from numpy import trapz
from matplotlib.patches import Rectangle

mean = 0
variance = 1
sigma = sqrt(variance)
Z = 1.28
alternate_mean = 5

# formula for Gaussian distribution, proof of God
def get_y(x, mean, variance, sigma):
	return (e**((-(x-mean)**2)/2*variance))/(sigma * sqrt(2*pi))

# suggestion from stack overflow, returns the index of the nearest matching value
def get_x_index(xs, Z):
	return min(range(len(xs)), key=lambda i: abs(xs[i]-Z))

def graph_normal(mean, variance):
	fig = plt.figure()
	xs = list(np.linspace(-4, 4, 1000))
	ys = [get_y(x, mean, variance, sigma) for x in xs]
	plt.plot(xs, ys)
	plt.vlines(0, ymin=0, ymax=max(ys))
	plt.xlabel("Mean Values")
	plt.title("Standard Gaussian")
	return fig, xs, ys

def fill_graph(fig, xs, ys, Z, outside=False, inside=False):
	idx = get_x_index(xs, Z)
	y_max =  ys[idx]
	outside_range = 0 < ys < y_max
	inside_range = 0 < ys > y_max
	if outside:
		plt.fill_between(xs, 0, ys, where=outside_range, facecolor="blue")
		area = trapz(ys[0:-idx], dx=0.0001) + trapz(ys[idx:], dx=0.0001)
	if inside:
		plt.fill_between(xs, 0, ys, where=inside_range, facecolor="blue")
		area = trapz(ys[-idx:idx], dx=0.0001)
	proportion = round(area / trapz(ys, dx=0.0001), 2)
	patch = Rectangle((0,0), 1, 1, facecolor="blue")
	plt.legend([patch], ["Area under the curve {}".format(proportion)])
	return fig

def add_gaussian(fig, alternate_mean, variance):
	sigma = sqrt(variance)
	lb = alternate_mean - 4
	ub = alternate_mean + 4
	xs = list(np.linspace(lb, ub, 1000))
	ys = [get_y(i, alternate_mean, variance, sigma) for i in xs]
	plt.vlines(alternate_mean, ymin=0, ymax=max(ys))
	plt.plot(xs, ys)
	return fig

fig, xs, ys = graph_normal(mean, variance)
fig = add_gaussian(fig, alternate_mean, variance)
fig = fill_graph(fig, xs, ys, Z, inside=True)
#fig.savefig("Graphs/Shaded_Inside.png")


import matplotlib.pyplot as plt
import numpy as np
import numpy
def linearCombo(a, b, c):
    '''This function is for visualizing linear combination of standard basis in 3D.
    Function syntax: linearCombo(a, b, c), where a, b, c are the scalar multiplier,
    also the elements of the vector.
    '''
    fig = plt.figure(figsize = (10,10))
    ax = fig.add_subplot(projection='3d')

    ######################## Standard basis and Scalar Multiplid Vectors#########################
    vec = np.array([[[0, 0, 0, 1, 0, 0]], # e1
                    [[0, 0, 0, 0, 1, 0]], # e2
                    [[0, 0, 0, 0, 0, 1]], # e3
                    [[0, 0, 0, a, 0, 0]], # a* e1
                    [[0, 0, 0, 0, b, 0]], # b* e2
                    [[0, 0, 0, 0, 0, c]], # c* e3
                    [[0, 0, 0, a, b, c]]]) # ae1 + be2 + ce3
    colors = ['b','b','b','r','r','r','g']
    for i in range(vec.shape[0]):
        X, Y, Z, U, V, W = zip(*vec[i,:,:])
        ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False,
                  color = colors[i] ,arrow_length_ratio = .08, pivot = 'tail',
                  linestyles = 'solid',linewidths = 3, alpha =.6)

    #################################Plot Rectangle Boxes##############################
    dlines = np.array([[[a, 0, 0],[a, b, 0]],
                      [[0, b, 0],[a, b, 0]],
                      [[0, 0, c],[a, b, c]],
                      [[0, 0, c],[a, 0, c]],
                      [[a, 0, c],[a, b, c]],
                      [[0, 0, c],[0, b, c]],
                      [[0, b, c],[a, b, c]],
                      [[a, 0, 0],[a, 0, c]],
                      [[0, b, 0],[0, b, c]],
                      [[a, b, 0],[a, b, c]]])
    colors = ['k','k','g','k','k','k','k','k','k']
    for i in range(dlines.shape[0]):
        ax.plot(dlines[i,:,0], dlines[i,:,1], dlines[i,:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    #################################Annotation########################################
    ax.text(x = a, y = b, z = c, s= ' $(%0.d, %0.d, %.0d)$'% (a, b, c), size = 18)
    ax.text(x = a, y = 0, z = 0, s= ' $%0.d e_1 = (%0.d, 0, 0)$'% (a, a), size = 15)
    ax.text(x = 0, y = b, z = 0, s= ' $%0.d e_2 = (0, %0.d, 0)$'% (b, b), size = 15)
    ax.text(x = 0, y = 0, z = c, s= ' $%0.d e_3 = (0, 0, %0.d)$' %(c, c), size = 15)

    #################################Axis Setting######################################
    ax.grid()
    ax.set_xlim([0, a+1])
    ax.set_ylim([0, b+1])
    ax.set_zlim([0, c+1])

    ax.set_xlabel('x-axis', size = 18)
    ax.set_ylabel('y-axis', size = 18)
    ax.set_zlabel('z-axis', size = 18)

    ax.set_title('Vector $(%0.d, %0.d, %.0d)$ Visualization' %(a, b, c), size = 20)

    ax.view_init(elev=20., azim=15)

if __name__ == '__main__':
    a = 7
    b = 4
    c = 9
    linearCombo(a, b, c)

def linearComboNonStd(a, b, c, vec1, vec2, vec3):
    '''This function is for visualizing linear combination of non-standard basis in 3D.
    Function syntax: linearCombo(a, b, c, vec1, vec2, vec3), where a, b, c are the scalar multiplier,
    ve1, vec2 and vec3 are the basis.
    '''
    fig = plt.figure(figsize = (10,10))
    ax = fig.add_subplot(projection='3d')
    ########################Plot basis##############################
    vec1 = np.array([[0, 0, 0, vec1[0], vec1[1], vec1[2]]])
    X, Y, Z, U, V, W = zip(*vec1)
    ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'blue',arrow_length_ratio = .08, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

    vec2 = np.array([[0, 0, 0, vec2[0], vec2[1], vec2[2]]])
    X, Y, Z, U, V, W = zip(*vec2)
    ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'blue',arrow_length_ratio = .08, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

    vec3 = np.array([[0, 0, 0, vec3[0], vec3[1], vec3[2]]])
    X, Y, Z, U, V, W = zip(*vec3)
    ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'blue',arrow_length_ratio = .08, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

    ###########################Plot Scalar Muliplied Vectors####################
    avec1 = a * vec1
    X, Y, Z, U, V, W = zip(*avec1)
    ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'red', alpha = .6,arrow_length_ratio = a/100, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

    bvec2 = b * vec2
    X, Y, Z, U, V, W = zip(*bvec2)
    ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'red', alpha = .6,arrow_length_ratio = b/100, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

    cvec3 = c * vec3
    X, Y, Z, U, V, W = zip(*cvec3)
    ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'red', alpha = .6,arrow_length_ratio = c/100, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

    combo = avec1 + bvec2 + cvec3
    X, Y, Z, U, V, W = zip(*combo)
    ax.quiver(X, Y, Z, U, V, W, length=1, normalize=False, color = 'green', alpha = .7,arrow_length_ratio = np.linalg.norm(combo)/300, pivot = 'tail',
          linestyles = 'solid',linewidths = 3)

    #################################Plot Rectangle Boxes##############################
    point1 = [avec1[0, 3], avec1[0, 4], avec1[0, 5]]
    point2 = [avec1[0, 3]+bvec2[0, 3], avec1[0, 4]+bvec2[0, 4], avec1[0, 5]+bvec2[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    point1 = [bvec2[0, 3], bvec2[0, 4], bvec2[0, 5]]
    point2 = [avec1[0, 3]+bvec2[0, 3], avec1[0, 4]+bvec2[0, 4], avec1[0, 5]+bvec2[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    point1 = [bvec2[0, 3], bvec2[0, 4], bvec2[0, 5]]
    point2 = [cvec3[0, 3]+bvec2[0, 3], cvec3[0, 4]+bvec2[0, 4], cvec3[0, 5]+bvec2[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    point1 = [cvec3[0, 3], cvec3[0, 4], cvec3[0, 5]]
    point2 = [cvec3[0, 3]+bvec2[0, 3], cvec3[0, 4]+bvec2[0, 4], cvec3[0, 5]+bvec2[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    point1 = [cvec3[0, 3], cvec3[0, 4], cvec3[0, 5]]
    point2 = [cvec3[0, 3]+avec1[0, 3], cvec3[0, 4]+avec1[0, 4], cvec3[0, 5]+avec1[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    point1 = [avec1[0, 3], avec1[0, 4], avec1[0, 5]]
    point2 = [cvec3[0, 3]+avec1[0, 3], cvec3[0, 4]+avec1[0, 4], cvec3[0, 5]+avec1[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    ##
    point1 = [avec1[0, 3]+bvec2[0, 3]+cvec3[0, 3],
              avec1[0, 4]+bvec2[0, 4]+cvec3[0, 4],
              avec1[0, 5]+bvec2[0, 5]+cvec3[0, 5]]
    point2 = [cvec3[0, 3]+avec1[0, 3],
              cvec3[0, 4]+avec1[0, 4],
              cvec3[0, 5]+avec1[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    ##
    point1 = [avec1[0, 3]+bvec2[0, 3]+cvec3[0, 3],
              avec1[0, 4]+bvec2[0, 4]+cvec3[0, 4],
              avec1[0, 5]+bvec2[0, 5]+cvec3[0, 5]]
    point2 = [cvec3[0, 3]+bvec2[0, 3],
              cvec3[0, 4]+bvec2[0, 4],
              cvec3[0, 5]+bvec2[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)

    ##
    point1 = [avec1[0, 3]+bvec2[0, 3]+cvec3[0, 3],
              avec1[0, 4]+bvec2[0, 4]+cvec3[0, 4],
              avec1[0, 5]+bvec2[0, 5]+cvec3[0, 5]]
    point2 = [bvec2[0, 3]+avec1[0, 3],
              bvec2[0, 4]+avec1[0, 4],
              bvec2[0, 5]+avec1[0, 5]]
    line1 = np.array([point1, point2])
    ax.plot(line1[:,0], line1[:,1], line1[:,2], lw =3, ls = '--', color = 'black', alpha=0.5)
    #################################Annotation########################################
    ax.text(x = vec1[0,3], y = vec1[0,4], z = vec1[0,5], s= ' $v_1 =(%0.d, %0.d, %.0d)$'% (vec1[0,3], vec1[0,4], vec1[0,4]), size = 8)
    ax.text(x = vec2[0,3], y = vec2[0,4], z = vec2[0,5], s= ' $v_2 =(%0.d, %0.d, %.0d)$'% (vec2[0,3], vec2[0,4], vec2[0,4]), size = 8)
    ax.text(x = vec3[0,3], y = vec3[0,4], z = vec3[0,5], s= ' $v_3= (%0.d, %0.d, %.0d)$'% (vec3[0,3], vec3[0,4], vec3[0,4]), size = 8)

    ax.text(x = avec1[0,3], y = avec1[0,4], z = avec1[0,5], s= ' $%.0d v_1 =(%0.d, %0.d, %.0d)$'% (a, avec1[0,3], avec1[0,4], avec1[0,4]), size = 8)
    ax.text(x = bvec2[0,3], y = bvec2[0,4], z = bvec2[0,5], s= ' $%.0d v_2 =(%0.d, %0.d, %.0d)$'% (b, bvec2[0,3], bvec2[0,4], bvec2[0,4]), size = 8)
    ax.text(x = cvec3[0,3], y = cvec3[0,4], z = cvec3[0,5], s= ' $%.0d v_3= (%0.d, %0.d, %.0d)$'% (c, cvec3[0,3], cvec3[0,4], cvec3[0,4]), size = 8)
#     ax.text(x = 0, y = b, z = 0, s= ' $%0.d e_2 = (0, %0.d, 0)$'% (b, b), size = 15)
#     ax.text(x = 0, y = 0, z = c, s= ' $%0.d e_3 = (0, 0, %0.d)$' %(c, c), size = 15)

    #################################Axis Setting######################################
    ax.grid()
    ax.set_xlim([0, 15])
    ax.set_ylim([0, 15])
    ax.set_zlim([0, 15])

    ax.set_xlabel('x-axis', size = 18)
    ax.set_ylabel('y-axis', size = 18)
    ax.set_zlabel('z-axis', size = 18)

    #ax.set_title('Vector $(%0.d, %0.d, %.0d)$ Visualization' %(a, b, c), size = 20)

    ax.view_init(elev=20., azim=15)

if __name__ == '__main__':
    a = 2
    b = 3
    c = 4
    vec1 = np.array([2,1,0])
    vec2 = np.array([0,3,1])
    vec3 = np.array([1,2,3])
    linearComboNonStd(a, b, c, vec1,vec2,vec3)


import numpy as np

class KMeans:
    def __init__(self, cluster_num, sample_num_all, max_iter=100):
        self.cluster_num = cluster_num
        self.max_iter = max_iter
        self.sample_num_all = sample_num_all
        self.cluster_centers = None

    def generate_init_center(self, data):
        idx = np.random.choice(self.sample_num_all, self.cluster_num, replace=False)
        self.cluster_centers = np.vstack([data[i] for i in idx])

    def find_nearest_center(self, point):
        min_dist = float('inf')
        label = 0
        for i in range(self.cluster_num):
            dist = np.linalg.norm(point-self.cluster_centers[i])
            if dist < min_dist:
                min_dist = dist
                label = i
        return label

    def fit(self, data):
        self.generate_init_center(data)
        label_all = []
        for it in range(self.max_iter):
            label_all = []
            for id in range(self.sample_num_all):
                label = self.find_nearest_center(data[id])
                label_all.append(label)
            label_all = np.array(label_all)
            for i in range(self.cluster_num):
                idx = np.argwhere(label_all == i)
                points = np.vstack([data[idx]])
                self.cluster_centers[i] = np.mean(points, axis=0)
        return label_all, self.cluster_centers


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import style
from scipy import linalg
from scipy.stats import multivariate_normal
import pyprobml_utils as pml

mu_1 = np.array([[0.22], [0.45]])
mu_2 = np.array([[0.5], [0.5]])
mu_3 = np.array([[0.77], [0.55]])
Mu = np.array([mu_1, mu_2, mu_3])

Sigma1 = np.array([[0.011, -0.01], [-0.01, 0.018]])
Sigma2 = np.array([[0.018, 0.01],  [0.01, 0.011]])
Sigma3 = Sigma1
Sigma = np.array([Sigma1, Sigma2, Sigma3])
mixmat = np.array([[0.5], [0.3], [0.2]]);

def sigmaEllipse2D(mu, Sigma, level=3, npoints=128):
    '''
    SIGMAELLIPSE2D generates x,y-points which lie on the ellipse describing
    a sigma level in the Gaussian density defined by mean and covariance.

    Input:
        MU          [2 x 1] Mean of the Gaussian density
        SIGMA       [2 x 2] Covariance matrix of the Gaussian density
        LEVEL       Which sigma level curve to plot. Can take any positive value,
                    but common choices are 1, 2 or 3. Default = 3.
        NPOINTS     Number of points on the ellipse to generate. Default = 32.

    Output:
        XY          [2 x npoints] matrix. First row holds x-coordinates, second
                    row holds the y-coordinates. First and last columns should
                    be the same point, to create a closed curve.
    '''
    phi = np.linspace(0, 2*np.pi, npoints)
    x = np.cos(phi)
    y = np.sin(phi)
    z = level*np.vstack((x, y))
    xy = mu + linalg.sqrtm(Sigma).dot(z)
    return xy

def plot_sigma_levels(mu, P):
    xy_1 = sigmaEllipse2D(mu, P, 0.25)
    xy_2 = sigmaEllipse2D(mu, P, 0.5)
    xy_3 = sigmaEllipse2D(mu, P, 0.75)
    xy_4 = sigmaEllipse2D(mu, P, 1)
    xy_5 = sigmaEllipse2D(mu, P, 1.25)
    xy_6 = sigmaEllipse2D(mu, P, 1.5)

    plt.plot(xy_1[0], xy_1[1])
    plt.plot(xy_2[0], xy_2[1])
    plt.plot(xy_3[0], xy_3[1])
    plt.plot(xy_4[0], xy_4[1])
    plt.plot(xy_5[0], xy_5[1])
    plt.plot(xy_6[0], xy_6[1])
    plt.plot(mu[0], mu[1], 'ro')

def plot_sigma_vector(Mu, Sigma):
    n = len(Mu)
    plt.figure(figsize=(12, 7))
    for i in range(n):
        plot_sigma_levels(Mu[i], Sigma[i])
    plt.tight_layout()
    pml.savefig('mixgaussSurface.pdf')
    plt.show()

plot_sigma_vector(Mu, Sigma)

def plot_gaussian_mixture(Mu, Sigma, weights=None, x = None, y = None):
    if x == None:
        x = np.arange(0, 1, 0.01)
    if y == None:
        y = np.arange(-0.5, 1.2, 0.01)

    if len(Mu) == len(Sigma) == len(weights):
        pass
    else:
        print("Error: Mu, Sigma and weights must have the same dimension")
        return

    X, Y = np.meshgrid(x, y)
    Pos = np.dstack((X, Y))
    Z = 0

    for i in range(len(Mu)):
        Z = Z + weights[i] * multivariate_normal(Mu[i].ravel(), Sigma[i]).pdf(Pos)

    fig = plt.figure(figsize=(12, 7))
    ax = fig.gca(projection='3d')
    ax.plot_surface(X, Y, Z, cmap="copper", lw=0.5, rstride=1, cstride=1)
    ax.set_xlabel('X axis')
    ax.set_ylabel('Y axis')
    ax.set_zlabel('Z axis')
    plt.tight_layout()
    pml.savefig('mixgaussSurface.pdf')
    plt.show()

weights = [0.5, 0.3, 0.2]
plot_gaussian_mixture(Mu, Sigma, weights=weights)


"""
http://www.johnwittenauer.net/machine-learning-exercises-in-python-part-3/
"""

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from sklearn.metrics import classification_report

import os

# def predict(x, theta):
#     prob = sigmoid(x @ theta)
#     return (prob >= 0.5).astype(int)

def predict(theta, X):
    probability = sigmoid(X * theta.T)
    return [1 if x >= 0.5 else 0 for x in probability]

def sigmoid(z):
    return 1 / (1 + np.exp(-z))

def gradient(theta, X, y):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)

    parameters = int(theta.ravel().shape[1])
    grad = np.zeros(parameters)

    error = sigmoid(X * theta.T) - y

    for i in range(parameters):
        term = np.multiply(error, X[:, i])
        grad[i] = np.sum(term) / len(X)

    return grad

def cost(theta, X, y):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)
    first = np.multiply(-y, np.log(sigmoid(X * theta.T)))
    second = np.multiply((1 - y), np.log(1 - sigmoid(X * theta.T)))
    return np.sum(first - second) / (len(X))

if __name__ == '__main__':
    path = "D:\git\Coursera-ML\johnwittenauer\data\ex2data1.txt"
    data = pd.read_csv(path, header=None, names=['Exam 1', 'Exam 2', 'Admitted'])
    print(data.head())
    positive = data[data['Admitted'].isin([1])]
    negative = data[data['Admitted'].isin([0])]

    fig, ax = plt.subplots(figsize=(12, 8))
    ax.scatter(positive['Exam 1'], positive['Exam 2'], s=50, c='b', marker='o', label='Admitted')
    ax.scatter(negative['Exam 1'], negative['Exam 2'], s=50, c='r', marker='x', label='Not Admitted')
    ax.legend()
    ax.set_xlabel('Exam 1 Score')
    ax.set_ylabel('Exam 2 Score')

    # plt.show()

    nums = np.arange(-10, 10, step=0.1)

    fig, ax = plt.subplots(figsize=(12, 8))
    ax.plot(nums, sigmoid(nums), 'r')

    # add a ones column - this makes the matrix multiplication work out easier
    data.insert(0, 'Ones', 1)

    # set X (training data) and y (target variable)
    cols = data.shape[1]
    X = data.iloc[:, 0:cols - 1]
    y = data.iloc[:, cols - 1:cols]

    # convert to numpy arrays and initalize the parameter array theta
    X = np.array(X.values)
    y = np.array(y.values)
    theta = np.zeros(3)
    print(X.shape, theta.shape, y.shape)

    print(cost(theta, X, y))
    # plt.show()
    import scipy.optimize as opt

    result = opt.fmin_tnc(func=cost, x0=theta, fprime=gradient, args=(X, y))
    final_theta = result[0]
    print("final theta:", final_theta)
    print("cost:", cost(result[0], X, y))

    #report
    theta_min = np.matrix(final_theta)
    predictions = predict(theta_min, X)
    # print(predictions)

    print(classification_report(y, predictions))

    # theta_min = np.matrix(result[0])
    # predictions = predict(theta_min, X)
    # correct = [1 if ((a == 1 and b == 1) or (a == 0 and b == 0)) else 0 for (a, b) in zip(predictions, y)]
    # accuracy = (sum(map(int, correct)) % len(correct))
    # print ('accuracy = {0}%'.format(accuracy))


import numpy
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
import random

print('----------Question 1--------------------')
meanclassA = numpy.random.randn(3)
print('mean of classA', meanclassA)
meanclassB = numpy.random.randn(3)
print('mean of classB', meanclassB)
cov = [[100, 0, 0], [0, 50, 0], [0, 0, 1]]  # diagonal covariance
print('Covariance matrix', cov)

xclassA, yclassA, zclassA = numpy.random.multivariate_normal(
    meanclassA, cov, 1000).T

xclassB, yclassB, zclassB = numpy.random.multivariate_normal(
    meanclassB, cov, 1000).T

fig = plt.figure()
ax = fig.add_subplot(2, 2, 1, projection='3d')
ax.scatter(xclassA, yclassA, zclassA,label= "classA", color='red', marker='x')
ax.scatter(xclassB, yclassB, zclassB, label= "classB", color='blue', marker='o')
ax.set_xlabel('x axis')
ax.set_ylabel('y axis')
ax.set_zlabel('z axis')
plt.legend()

ax = fig.add_subplot(2, 2, 2, projection='3d')
ax.scatter(xclassA, yclassA, zclassA, label= "classA", color='red', marker='x')
ax.plot(xclassA, zclassA, 'y+', label= "xz projection", zdir='y', zs=-50)
ax.plot(yclassA, zclassA, 'g+', label= "yz projection", zdir='x', zs=-50)
ax.plot(xclassA, yclassA, 'k+', label= "xy projection", zdir='z', zs=-4)
plt.legend()

ax = fig.add_subplot(2, 2, 3, projection='3d')
ax.scatter(xclassB, yclassB, zclassB,label= "classB", color='blue', marker='o')
ax.plot(xclassB, zclassB, 'y+',  label= "xz projection", zdir='y', zs=-50)
ax.plot(yclassB, zclassB, 'g+',  label= "yz projection", zdir='x', zs=-50)
ax.plot(xclassB, yclassB, 'k+', label= "xy projection", zdir='z', zs=-4)
plt.legend()
plt.title('Question1, different mean, and diagonal covariance matrix')
plt.show()

print('----------Question 2--------------------')
matrixSize = 3
A = numpy.random.rand(matrixSize,matrixSize)
cov = numpy.dot(A,A.transpose()) #PSD Cov matrix
print('Cov matrix PSD', cov)
meanclassA = numpy.random.randn(3)
print('mean of classA', meanclassA)
meanclassB = numpy.random.randn(3)
print('mean of classB', meanclassB)

xclassA, yclassA, zclassA = numpy.random.multivariate_normal(
    meanclassA, cov, 1000).T

xclassB, yclassB, zclassB = numpy.random.multivariate_normal(
    meanclassB, cov, 1000).T

fig = plt.figure()
ax = fig.add_subplot(2, 2, 1, projection='3d')
ax.scatter(xclassA, yclassA, zclassA,label= "classA", color='red', marker='x')
ax.scatter(xclassB, yclassB, zclassB, label= "classB", color='blue', marker='o')
ax.set_xlabel('x axis')
ax.set_ylabel('y axis')
ax.set_zlabel('z axis')
plt.legend()

ax = fig.add_subplot(2, 2, 2, projection='3d')
ax.scatter(xclassA, yclassA, zclassA, label= "classA", color='red', marker='x')
ax.plot(xclassA, zclassA, 'y+', label= "xz projection", zdir='y', zs=-5)
ax.plot(yclassA, zclassA, 'g+', label= "yz projection", zdir='x', zs=-5)
ax.plot(xclassA, yclassA, 'k+', label= "xy projection", zdir='z', zs=-4)
plt.legend()

ax = fig.add_subplot(2, 2, 3, projection='3d')
ax.scatter(xclassB, yclassB, zclassB,label= "classB", color='blue', marker='o')
ax.plot(xclassB, zclassB, 'y+',  label= "xz projection", zdir='y', zs=-5)
ax.plot(yclassB, zclassB, 'g+',  label= "yz projection", zdir='x', zs=-5)
ax.plot(xclassB, yclassB, 'k+', label= "xy projection", zdir='z', zs=-4)
plt.legend()
plt.title('Question2, different mean, and PSD matrix')
plt.show()

print('----------Question 3--------------------')
matrixSize = 3
A = numpy.random.rand(matrixSize,matrixSize)
covClassA = numpy.dot(A,A.transpose()) #PSD Cov matrix
print('Covariance matrix for class A', covClassA)
A = numpy.random.rand(matrixSize,matrixSize)
covClassB = numpy.dot(A,A.transpose()) #PSD Cov matrix
print('Covariance matrix for class B', covClassB)
mean = numpy.random.randn(3)
print('mean', mean)

xclassA, yclassA, zclassA = numpy.random.multivariate_normal(
    mean, covClassA, 1000).T

xclassB, yclassB, zclassB = numpy.random.multivariate_normal(
    mean, covClassB, 1000).T

fig = plt.figure()
ax = fig.add_subplot(2, 2, 1, projection='3d')
ax.scatter(xclassA, yclassA, zclassA,label= "classA", color='red', marker='x')
ax.scatter(xclassB, yclassB, zclassB, label= "classB", color='blue', marker='o')
ax.set_xlabel('x axis')
ax.set_ylabel('y axis')
ax.set_zlabel('z axis')
plt.legend()

ax = fig.add_subplot(2, 2, 2, projection='3d')
ax.scatter(xclassA, yclassA, zclassA, label= "classA", color='red', marker='x')
ax.plot(xclassA, zclassA, 'y+', label= "xz projection", zdir='y', zs=-5)
ax.plot(yclassA, zclassA, 'g+', label= "yz projection", zdir='x', zs=-5)
ax.plot(xclassA, yclassA, 'k+', label= "xy projection", zdir='z', zs=-4)
plt.legend()

ax = fig.add_subplot(2, 2, 3, projection='3d')
ax.scatter(xclassB, yclassB, zclassB,label= "classB", color='blue', marker='o')
ax.plot(xclassB, zclassB, 'y+',  label= "xz projection", zdir='y', zs=-5)
ax.plot(yclassB, zclassB, 'g+',  label= "yz projection", zdir='x', zs=-5)
ax.plot(xclassB, yclassB, 'k+', label= "xy projection", zdir='z', zs=-4)
plt.legend()
plt.title('Question3, same mean, and different cov matrix')
plt.show()


# Problem: https://projecteuler.net/problem=164

"""
    Let DP[n,a,b] indicate the number of such a number described in the problem statement
                  that has n-digit and a and b being the last two digits.
    For a fixed b and c, DP[n,b,c] = sum_{a | a + b + c <= 9}(DP[n-1,a,b]).
"""

import numpy as np

N = 20

ans = 0

DP = np.zeros((N+1, 10, 10), dtype = np.int64)

for i in range(1, 10):
    DP[1, 0, i] = 1

for n in range(2, N+1):
    for a in range(10):
        for b in range(10):
            for c in range(9-a-b+1):
                DP[n, b, c] = DP[n, b, c] + DP[n-1, a, b]

for i in range(10):
    for j in range(10):
        ans = ans + DP[N, i, j]

print(ans)


"""

https://apmonitor.com/pdc/index.php/Main/ModelSimulation
https://apmonitor.com/pdc/index.php/Main/SecondOrderSystems

"""

import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
from scipy import signal
from sympy.abc import s, t
from sympy.integrals import inverse_laplace_transform
from math import *

# tau * dy2/dt2 + 2*zeta*tau*dy/dt + y = Kp*u
Kp = 2.0    # gain
tau = 1.0   # time constant
zeta = 0.5 # damping factor
theta = 0.0 # no time delay
du = 1.0    # change in u

# (1) Transfer Function
num = [Kp]
den = [tau**2,2*zeta*tau,1]
sys1 = signal.TransferFunction(num,den)
t1,y1 = signal.step(sys1)

# (2) State Space
A = [[0.0,1.0],[-1.0/tau**2,-2.0*zeta/tau]]
B = [[0.0],[Kp/tau**2]]
C = [1.0,0.0]
D = 0.0
sys2 = signal.StateSpace(A,B,C,D)
t2,y2 = signal.step(sys2)

# (3) ODE Integrator
def model3(x,t):
    y = x[0]
    dydt = x[1]
    dy2dt2 = (-2.0*zeta*tau*dydt - y + Kp*du)/tau**2
    return [dydt,dy2dt2]
t3 = np.linspace(0,25,100)
x3 = odeint(model3,[0,0],t3)
y3 = x3[:,0]

# (4) Closed Form
# ay'' + by' + cy = K*u(t-theta)
# u(t) is the unit step function
# a = tau*tau
# b = 2*zeta*tau
# c = 1
# theta = 0
# This differential equation has 3 different solutions
# based on the value of zeta
def closed(t):
    # magnitude of step function
    M = 1
    if zeta < 1: # underdamped
        z = sqrt(1 - zeta*zeta)
        A = cos(t/tau*z)
        B = zeta/z * sin(t/tau*z)
        v = 1 - exp(-zeta*t/tau)*(A + B)
    elif zeta > 1: # overdamped
        z = sqrt(zeta*zeta - 1)
        A = cosh(t/tau*z)
        B = zeta/z * sinh(t/tau*z)
        v = 1 - exp(-zeta*t/tau)*(A + B)
    else: # critically damped
        v = 1 - (1 + t/tau)*exp(-t/tau)
    return Kp*M*v
y4 = np.array([closed(t) for t in t3])

# (5) Symbolic
# Multiply transfer function G(s) with unit step U(s)
# then take the inverse laplace transform for equation
G = Kp/(tau*tau*s*s + 2*zeta*tau*s + 1)
U = 1/s
Y = inverse_laplace_transform(G*U, s, t)
y5 = np.array([Y.subs(t, ts) for ts in t3])

print("Transfer Function: {}".format(G))

plt.figure(1)
plt.plot(t1,y1*du,'b--',linewidth=5,label='Transfer Function')
plt.plot(t2,y2*du,'g:',linewidth=4,label='State Space')
plt.plot(t3,y3,'r-',linewidth=3,label='ODE Integrator')
plt.plot(t3,y4,'p-',linewidth=2,label='Closed Form')
plt.plot(t3,y5,'c-',linewidth=1,label='Symbolic')
y_ss = Kp * du
plt.plot([0,max(t1)],[y_ss,y_ss],'k:')
plt.xlim([0,max(t1)])
plt.xlabel('Time')
plt.ylabel('Response (y)')
plt.legend(loc='best')
plt.savefig('2nd_order.png')
plt.show()


"""
  Name     : c10_44_implied_vol_binary_search.py
  Book     : Python for Finance (2nd ed.)
  Publisher: Packt Publishing Ltd.
  Author   : Yuxing Yan
  Date     : 6/6/2017
  email    : yany@canisius.edu
             paulyxy@hotmail.com
"""

from scipy import log,exp,sqrt,stats
S=42;X=40;T=0.5;r=0.01;c=3.0
def bsCall(S,X,T,r,sigma):
    d1=(log(S/X)+(r+sigma*sigma/2.)*T)/(sigma*sqrt(T))
    d2 = d1-sigma*sqrt(T)
    return S*stats.norm.cdf(d1)-X*exp(-r*T)*stats.norm.cdf(d2)
#
def impliedVolBinary(S,X,T,r,c):
    k=1
    volLow=0.001
    volHigh=1.0
    cLow=bsCall(S,X,T,r,volLow)
    cHigh=bsCall(S,X,T,r,volHigh)
    if cLow>c or cHigh<c:
        raise ValueError
    while k ==1:
        cLow=bsCall(S,X,T,r,volLow)
        cHigh=bsCall(S,X,T,r,volHigh)
        volMid=(volLow+volHigh)/2.0
        cMid=bsCall(S,X,T,r,volMid)
        if abs(cHigh-cLow)<0.01:
            k=2
        elif cMid>c:
            volHigh=volMid
        else:
            volLow=volMid
    return volMid, cLow, cHigh
#
print("Vol,     cLow,      cHigh")
print(impliedVolBinary(S,X,T,r,c))


import math
from copy import deepcopy

import numpy as np

from DecisonTree import Leaf, Question, DecisionNode, class_counts, unique_vals
from utils import *

"""
Make the imports of python packages needed
"""

class ID3:
    def __init__(self, label_names: list, min_for_pruning=0, target_attribute='diagnosis'):
        self.label_names = label_names
        self.target_attribute = target_attribute
        self.tree_root = None
        self.used_features = set()
        self.min_for_pruning = min_for_pruning

    @staticmethod
    def entropy(rows: np.ndarray, labels: np.ndarray):
        """
        Calculate the entropy of a distribution for the classes probability values.
        :param rows: array of samples
        :param labels: rows data labels.
        :return: entropy value.
        """
        # TODO:
        #  Calculate the entropy of the data as shown in the class.
        #  - You can use counts as a helper dictionary of label -> count, or implement something else.

        counts = class_counts(rows, labels)
        impurity = 0.0

        # ====== YOUR CODE: ======
        entropy_val = 0
        num_of_labels = len(labels)
        for label, count in counts.items():
            entropy_val += count / num_of_labels * math.log2(count / num_of_labels)
        impurity = -1 * entropy_val
        # ========================

        return impurity

    def info_gain(self, left, left_labels, right, right_labels, current_uncertainty):
        """
        Calculate the information gain, as the uncertainty of the starting node, minus the weighted impurity of
        two child nodes.
        :param left: the left child rows.
        :param left_labels: the left child labels.
        :param right: the right child rows.
        :param right_labels: the right child labels.
        :param current_uncertainty: the current uncertainty of the current node
        :return: the info gain for splitting the current node into the two children left and right.
        """
        # TODO:
        #  - Calculate the entropy of the data of the left and the right child.
        #  - Calculate the info gain as shown in class.
        assert (len(left) == len(left_labels)) and (len(right) == len(right_labels)), \
            'The split of current node is not right, rows size should be equal to labels size.'

        info_gain_value = 0.0
        # ====== YOUR CODE: ======
        total_samples = len(left) + len(right)
        left_weight = len(left) / total_samples
        right_weight = len(right) / total_samples
        left_entropy = self.entropy(left, left_labels)
        right_entropy = self.entropy(right, right_labels)
        info_gain_value = current_uncertainty - (left_weight * left_entropy + right_weight * right_entropy)
        # ========================

        return info_gain_value

    def partition(self, rows, labels, question: Question, current_uncertainty):
        """
        Partitions the rows by the question.
        :param rows: array of samples
        :param labels: rows data labels.
        :param question: an instance of the Question which we will use to partition the data.
        :param current_uncertainty: the current uncertainty of the current node
        :return: Tuple of (gain, true_rows, true_labels, false_rows, false_labels)
        """
        # TODO:
        #   - For each row in the dataset, check if it matches the question.
        #   - If so, add it to 'true rows', otherwise, add it to 'false rows'.
        #   - Calculate the info gain using the `info_gain` method.

        gain, true_rows, true_labels, false_rows, false_labels = None, None, None, None, None
        assert len(rows) == len(labels), 'Rows size should be equal to labels size.'

        # ====== YOUR CODE: ======
        true_rows = []
        true_labels = []
        false_rows = []
        false_labels = []
        for row, label in zip(rows, labels):
            if question.match(row):
                true_rows.append(row)
                true_labels.append(label)
            else:
                false_rows.append(row)
                false_labels.append(label)
        gain = self.info_gain(false_rows, false_labels, true_rows, true_labels, current_uncertainty)
        # ========================

        return gain, true_rows, true_labels, false_rows, false_labels

    def find_best_split(self, rows, labels):
        """
        Find the best question to ask by iterating over every feature / value and calculating the information gain.
        :param rows: array of samples
        :param labels: rows data labels.
        :return: Tuple of (best_gain, best_question, best_true_rows, best_true_labels, best_false_rows, best_false_labels)
        """
        # TODO:
        #   - For each feature of the dataset, build a proper question to partition the dataset using this feature.
        #   - find the best feature to split the data. (using the `partition` method)
        best_gain = - math.inf  # keep track of the best information gain
        best_question = None  # keep train of the feature / value that produced it
        best_false_rows, best_false_labels = None, None
        best_true_rows, best_true_labels = None, None
        current_uncertainty = self.entropy(rows, labels)

        # ====== YOUR CODE: ======
        attributes_names = deepcopy(self.label_names)
        attributes_names.remove(self.target_attribute)
        for attribute_index, attribute_name in enumerate(attributes_names):
            values_for_column = unique_vals(rows, attribute_index)
            values_for_column = list(values_for_column)
            values_for_column.sort()
            for val1, val2 in zip(values_for_column[:-1], values_for_column[1:]):
                avg_value = (val1 + val2) / 2
                temp_question = Question(attribute_name, attribute_index, avg_value)
                t_gain, t_true_rows, t_true_labels, t_false_rows, t_false_labels = self.partition(rows, labels,
                                                                                temp_question,
                                                                                current_uncertainty)
                if t_gain >= best_gain:
                    best_gain = t_gain
                    best_question = temp_question
                    best_false_rows = t_false_rows
                    best_false_labels = t_false_labels
                    best_true_rows = t_true_rows
                    best_true_labels = t_true_labels
        # ========================

        return best_gain, best_question, best_true_rows, best_true_labels, best_false_rows, best_false_labels

    def build_tree(self, rows, labels):
        """
        Build the decision Tree in recursion.
        :param rows: array of samples
        :param labels: rows data labels.
        :return: a Question node, This records the best feature / value to ask at this point, depending on the answer.
                or leaf if we have to prune this branch (in which cases ?)

        """
        # TODO:
        #   - Try partitioning the dataset using the feature that produces the highest gain.
        #   - Recursively build the true, false branches.
        #   - Build the Question node which contains the best question with true_branch, false_branch as children
        best_question = None
        true_branch, false_branch = None, None

        # ====== YOUR CODE: ======
        best_gain, best_question, best_true_rows, best_true_labels, best_false_rows, best_false_labels = self.find_best_split(
            rows, labels)
        if best_question is None:
            return Leaf(rows, labels)
        if len(best_true_rows) <= self.min_for_pruning or len(class_counts(best_true_rows, best_true_labels)) == 1:
            true_branch = Leaf(best_true_rows, best_true_labels)
        else:
            true_branch = self.build_tree(best_true_rows, best_true_labels)
        if len(best_false_rows) <= self.min_for_pruning or len(class_counts(best_false_rows, best_false_labels)) == 1:
            false_branch = Leaf(best_false_rows, best_false_labels)
        else:
            false_branch = self.build_tree(best_false_rows, best_false_labels)
        self.used_features.add(best_question.column)
        # ========================

        return DecisionNode(best_question, true_branch, false_branch)

    def fit(self, x_train, y_train):
        """
        Trains the ID3 model. By building the tree.
        :param x_train: A labeled training data.
        :param y_train: training data labels.
        """
        # TODO: Build the tree that fits the input data and save the root to self.tree_root

        # ====== YOUR CODE: ======
        self.tree_root = self.build_tree(x_train, y_train)
        # ========================

    def predict_sample(self, row, node: DecisionNode or Leaf = None):
        """
        Predict the most likely class for single sample in subtree of the given node.
        :param row: vector of shape (1,D).
        :return: The row prediction.
        """
        # TODO: Implement ID3 class prediction for set of data.
        #   - Decide whether to follow the true-branch or the false-branch.
        #   - Compare the feature / value stored in the node, to the example we're considering.

        if node is None:
            node = self.tree_root
        prediction = None

        # ====== YOUR CODE: ======
        # Traverse tree nodes as long as we didn't arrive at a leaf
        while isinstance(node, DecisionNode):
            # This is a tree node, so we need to go down the tree based on the row value
            # print(Question)
            # print(row)
            if node.question.match(row):
                # Going to true branch
                node = node.true_branch
            else:
                # Going to false branch
                node = node.false_branch
        if isinstance(node, Leaf):
            # Reached a leaf we can classify by
            max_label_count = -np.inf
            label_count_dict = node.predictions
            for label, label_count in label_count_dict.items():
                if label_count >= max_label_count:
                    max_label_count = label_count
                    prediction = label
        # ========================

        return prediction

    def predict(self, rows):
        """
        Predict the most likely class for each sample in a given vector.
        :param rows: vector of shape (N,D) where N is the number of samples.
        :return: A vector of shape (N,) containing the predicted classes.
        """
        # TODO:
        #  Implement ID3 class prediction for set of data.

        y_pred = None

        # ====== YOUR CODE: ======
        y_pred = np.array([])
        for row in rows:
            y_pred = np.append(y_pred, self.predict_sample(row, None))
        # ========================

        return y_pred


# -*- coding: utf-8 -*-
"""LogisticRegression.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1Pymr8RvZdZkyufT_rl1nNGpH-1CkjNup

Same as linear Regression except , we pass the output through sigmoid activation function for classification
"""

import numpy as np

class LogisticRegression:

    def __init__(self , learning_rate = 0.001 , n_iters = 1000):
        self.learning_rate = learning_rate
        self.n_iters = n_iters
        self.weights = None
        self.bias = None

    def sigmoid_activation(self , x):
        return 1/(1+np.exp(-x))

    def fit(self , X , y):
        n_samples , n_features = X.shape

        #initializing weights and bias
        self.weights = np.zeros(n_features)
        self.bias = 0

        for i in range(n_samples):
            #applying approximation function for prediction
            linear_prediction = np.dot(X , self.weights)+self.bias

            #passing the output of logistic  regression model through a sigmoid function
            logistic_prediction = self.sigmoid_activation(linear_prediction)

            #calculating wwights and bias gradients (this tells the direction in which algo should proceed to reach the global minima)
            dw = (1/n_samples)*np.dot(2*X.T, (logistic_prediction - y))
            db = (1/n_samples) * 2*np.sum(logistic_prediction-y)

            #adjusting weights and bias
            self.weights -= self.learning_rate*dw
            self.bias -= self.learning_rate*db

    def predict(self , X):
        #applying approximation function for prediction
        linear_prediction = np.dot(X , self.weights)+self.bias

        #passing the output of logistic  regression model through a sigmoid function
        y_pred = self.sigmoid_activation(linear_prediction)

        classes = [1 if i > 0.5 else 0 for i in y_pred]

        return classes

from sklearn.model_selection import train_test_split
from sklearn import datasets

def accuracy(y_true, y_pred):
    accuracy = np.sum(y_true == y_pred) / len(y_true)
    return accuracy

bc = datasets.load_breast_cancer()
X, y = bc.data, bc.target

X_train, X_test, y_train, y_test = train_test_split(
        X, y, test_size=0.2, random_state=1234
    )

regressor = LogisticRegression(learning_rate=0.0001, n_iters=1000)
regressor.fit(X_train, y_train)
predictions = regressor.predict(X_test)

print("LR classification accuracy:", accuracy(y_test, predictions))

from sklearn.metrics import confusion_matrix
y_true = y
y_pred = regressor.predict(X)
confusion_matrix(y_true, y_pred)


import scipy as sp
import numpy as np
from scipy.fftpack import fft, ifft
from scipy.io import wavfile
from matplotlib import pyplot as plt
from pyfftw.interfaces import scipy_fftpack as fftw

#==============================================================================
# PROBLEM 1
#==============================================================================
def prob1():
    samplerate=44100 # 44100 samples per second
    freq=60   #60Hz sine wave
    length=1 # ... which will last for 1 seconds.
    stepsize=freq*2*sp.pi/samplerate
    sig=sp.sin(sp.arange(0,stepsize*length*samplerate,stepsize))
    scaled = sp.int16(sig/sp.absolute(sig).max() * 32767)
    plt.plot(scaled)
    plt.show()
    plt.close()
    wavfile.write('sine.wav',samplerate,scaled)
#==============================================================================
# PROBLEM 2 - Naive FT
#==============================================================================
def myft(f):
    N = f.size
    c = sp.zeros((N,), dtype=sp.complex128)
    for k in xrange(0,N):
        c[k] = (1/float(N))*(sp.exp(-2*sp.pi*1j*k*sp.arange(N)/float(N))*f).sum()
#        c[k] = (f * sp.exp(-2*sp.pi*1j*k*sp.arange(N)/N)).sum()
    return c
#==============================================================================
# PROBLEM 3
#==============================================================================
def prob3():
    rate, sig = wavfile.read('pianoclip.wav')
    sig = sp.float32(sig)
    fsig = fftw.fft(sig.T).T
    f = sp.absolute(fsig)
    plt.plot(f[0:f.shape[0]/2])
    plt.show() #spike at about 3088
    freq = rate/float(sig.shape[0]) * 3088
#note is approximately an f#5
#==============================================================================
# Problem 4
#==============================================================================
def prob4():
    old_rate, in_sig = wavfile.read('saw.wav')
    new_rate = 11025
    fin = fftw.fft(sp.float32(in_sig))
    nsiz = sp.floor(in_sig.size*new_rate/old_rate)
    nsizh = sp.floor(nsiz/2)
    fout = sp.zeros(nsiz) + 0j
    fout[0:nsizh] = fin[0:nsizh]
    fout[nsiz-nsizh+1:] = sp.conj(sp.flipud(fout[1:nsizh]))
    out = sp.ifft(fout)
    out = sp.real(out)
    out = sp.int16(out/sp.absolute(out).max() * 32767)
    wavfile.write('prob4_saw.wav',new_rate,out)
#===============================================================================
# Problem 5
#==============================================================================
def prob5():
    old_rate, in_sig = wavfile.read('saw.wav')
    new_rate = 36000
    fin = fftw.fft(sp.float32(in_sig))
    nsiz = sp.floor(in_sig.size*new_rate/old_rate)
    nsizh = sp.floor(nsiz/2)
    fout = sp.zeros(nsiz) + 0j
    fout[0:nsizh] = fin[0:nsizh]
    fout[nsiz-nsizh+1:] = sp.conj(sp.flipud(fout[1:nsizh]))
    out = fftw.ifft(fout)
    out = sp.real(out)
    out = sp.int16(out/sp.absolute(out).max() * 32767)
    wavfile.write('prob5_saw.wav',new_rate,out)
#===============================================================================


# import lib
from numpy import arange
from pandas import read_csv
from scipy.optimize import curve_fit
from matplotlib import pyplot

# define the true objective function
def objective(x, a, b):
	return a * x + b

# set data
data = [
        [10, 15.4],
	[19, 19.4],
	[28, 10.8],
	[37, 30.2],
	[46, 11.7],
	[55, 49.3],
	[64, 8.86],
	[73, 61.1],
	[82, 69.3],
	[91, 75]
]

x = []
y = []

for i in range(0, len(data)):
    value = data[i]
    x.append(value[0])
    y.append(value[1])

print(x)
print(y)

# # load the dataset from online
# url = 'https://raw.githubusercontent.com/jbrownlee/Datasets/master/longley.csv'
# dataframe = read_csv(url, header=None)
# data = dataframe.values
# # choose the input and output variables
# x, y = data[:, 4], data[:, -1]
# print(x)
# print(y)
# curve fit
popt, _ = curve_fit(objective, x, y)
# summarize the parameter values
a, b = popt
print('y = %.5f * x + %.5f' % (a, b))
# plot input vs output
pyplot.scatter(x, y)
# define a sequence of inputs between the smallest and largest known inputs
x_line = arange(min(x), max(x), 1)
# calculate the output for the range
y_line = objective(x_line, a, b)
# create a line plot for the mapping function
title = 'Trend Line Equation: y = %.5f * x + %.5f' % (a, b)
pyplot.plot(x_line, y_line, '--', color='red')
pyplot.title(title)
pyplot.show()


# backwards compatibility imports that allow using both python 2 and 3
from __future__ import print_function
from __future__ import division

import numpy as np

def get_plane(coords):
    """
    three reference points points in form

    np.array(
    [
      [x_1, y_1, z_1],
      [x_2, y_2, z_2],
      [x_3, y_3, z_3]
    ]
    )

    make sure that three points are not in one line
      as this "breaks" math used here.
    """

    # variables expanded
    x_1, y_1, z_1 = coords[0]
    x_2, y_2, z_2 = coords[1]
    x_3, y_3, z_3 = coords[2]

    # first we get vectors between points:
    # p_1 to p_2
    r_12 = x_2-x_1, y_2-y_1, z_2-z_1
    # p_1 to p_3
    r_13 = x_3-x_1, y_3-y_1, z_3-z_1

    # Now we compute the normal vector of the plane
    n = (
        r_12[1]*r_13[2] - r_12[2]*r_13[1],
        r_12[2]*r_13[0] - r_12[0]*r_13[2],
        r_12[0]*r_13[1] - r_12[1]*r_13[0],
        )

    # We add values to get plane equation
    plane = (
        n[0],
        n[1],
        n[2],
        n[0]*x_1+n[1]*y_1+n[2]*z_1
        )
    return plane

def get_point_projection(plane, p):
    """
    plane - defined by plane equation parameters
    p - point np.array([x, y, z])
    """
    # plane equation parameters expanded
    a, b, c, d = plane

    # original point coordinates expanded as (x0, y0, z0)
    x0, y0, z0 = p

    # now we need any point on a plane
    # let's denote it (px, py, pz)
    # try/except clauses that take care of potential errors with zero division
    try:
        px, py, pz = 0, 0, d/c
    except ZeroDivisionError:
        try:
            px, py, pz = 0, d/b, 0
        except ZeroDivisionError:
            px, py, pz = d/a, 0, 0

    # parameter t that is intermediate step in calculation of projection
    t = (a*px-a*x0 + b*py-b*y0 + c*pz-c*z0)/(a**2+b**2+c**2)

    # point projected on the plane has coordinates (x, y, z)
    x = x0+t*a
    y = y0+t*b
    z = z0+t*c

    return np.array((x, y, z), dtype=float)

def get_projections(plane, coords):
    """
    plane - defined by plane equation parameters

    coords - array of point coordinates shaped as follows:
    np.array(
        [
            [x_1, y_1, z_1],
            [x_2, y_2, z_2],
            [x_3, y_3, z_3],
            ...
        ]
    )
    """
    projections = np.array(
        list(
            map(
                lambda p: get_point_projection(plane, p),
                coords
                )
            ),
        dtype=float
    )
    return projections

def get_length(coords):
    """
    calculates length of a line connecting all points in:
    coords - points shaped as follows
    np.array(
        [
            [x_1, y_1, z_1],
            [x_2, y_2, z_2],
            [x_3, y_3, z_3]
            ...
        ]
    )
    """
    length = 0
    for i in range(1, coords.shape[0]):
        # coords[i] point i
        # coords[i-1] point i-1

        length += np.linalg.norm(coords[i]-coords[i-1])
    return length


from numba import jit
import numpy as np

@jit(nopython=True)
def RK4(odefun,ics,h,span,degree):

    N= int( (span[1]-span[0])/h )

    tY = np.zeros((N+1,degree+1))
    tY[0,1:] = ics

    for  i in range(N):
        tY[i+1,0] = tY[i,0] + h

        k1= odefun(tY[i,0]       , tY[i,1:])
        k2= odefun(tY[i,0] +(h/2), tY[i,1:] +(h*k1)/2 )
        k3= odefun(tY[i,0] +(h/2), tY[i,1:] +(h*k2)/2)
        k4= odefun(tY[i,0] +(h)  , tY[i,1:] +(h*k3))

        tY[i+1,1:] = tY[i,1:] + h*(1/6) * (k1+2*k2+2*k3+k4)

    return tY[:,0],tY[:,1:]


import numpy as np
import matplotlib.pyplot as plt
from sklearn.cross_validation import KFold
from scipy.optimize import fsolve

data = np.genfromtxt('web_traffic.tsv', delimiter='\t')
print data.shape
print data[:10]

x = data[:, 0]
y = data[:, 1]
print np.sum(np.isnan(y))

x = x[~np.isnan(y)]
y = y[~np.isnan(y)]

plt.scatter(x, y, s=10)
plt.title('Page views over last month')
plt.xlabel('Time')
plt.ylabel('PV/hour')
# set x ticks by week count
plt.xticks([w*7*24 for w in range(10)], ['week %d'%w for w in range(10)])
plt.autoscale(tight=True)
plt.grid(True, linestyle='-', color='0.75')
# plt.show()

def error(f, x, y):
    return np.sum(np.power(f(x) - y, 2))

fp1 = np.polyfit(x, y, 1)
print fp1  # [   2.59619213  989.02487106]
f1 = np.poly1d(fp1)
print type(f1)  # <class 'numpy.lib.polynomial.poly1d'>
print f1  # 2.596 x + 989
print error(f1, x, y)  # (317389767.34+0j)

# fx = np.linspace(0, x[-1], 1000)
fx = np.linspace(0, 6*7*24, 1000)
plt.ylim(0, 10000)
plt.plot(fx, f1(fx), linewidth=4)
# plt.legend(['d = %d' % f1.order], loc='upper left')
# plt.show()

fp2 = np.polyfit(x, y, 2)
print fp2  # [  1.05322215e-02  -5.26545650e+00   1.97476082e+03]
f2 = np.poly1d(fp2)
print type(f2)  # <class 'numpy.lib.polynomial.poly1d'>
print f2  # 0.01053 x^2 - 5.265 x + 1975
print error(f2, x, y)  # (179983507.878+0j)

plt.plot(fx, f2(fx), linewidth=4)
# plt.legend(['d = %d' % f.order for f in [f1, f2]], loc='upper left')
# plt.show()

fp3 = np.polyfit(x, y, 3)
f3 = np.poly1d(fp3)
print error(f3, x, y)
plt.plot(fx, f3(fx), linewidth=4)

fp10 = np.polyfit(x, y, 10)
f10 = np.poly1d(fp10)
print error(f10, x, y)
plt.plot(fx, f10(fx), linewidth=4)

fp50 = np.polyfit(x, y, 50)
f50 = np.poly1d(fp50)
print error(f50, x, y)
plt.plot(fx, f50(fx), linewidth=4)

fs = [f1, f2, f3, f10, f50]
plt.legend(['d = %d' % f.order for f in fs], loc='upper left', prop={'size': 10})

table = plt.table(cellText=[['%.2e' % error(f, x, y) for f in fs]],
    colWidths=[0.13] * len(fs),
    rowLabels=['error'],
    colLabels=['order %d' % f.order for f in fs],
    loc='upper right', zorder=100)
table.scale(1, 1.5)
# plt.show()

def gen_model(x, y, degree):
    fp = np.polyfit(x, y, degree)
    return np.poly1d(fp)

plt.figure()
plt.scatter(x, y, s=10)
plt.title('Page views over last month')
plt.xlabel('Time')
plt.ylabel('PV/hour')
# set x ticks by week count
plt.xticks([w*7*24 for w in range(10)], ['week %d'%w for w in range(10)])
plt.autoscale(tight=True)
plt.grid(True, linestyle='-', color='0.75')
plt.ylim(0, 10000)
y_lastweek = y[x > 3.5*7*24]
x_lastweek = x[x > 3.5*7*24]
degrees = [1, 2, 3, 10, 50]
fs = [gen_model(x_lastweek, y_lastweek, i) for i in degrees]
for f in fs:
    plt.plot(fx, f(fx), linewidth=4)
plt.legend(['d = %d' % f.order for f in fs], loc='upper left', prop={'size': 10})
table = plt.table(cellText=[['%.2e' % error(f, x, y) for f in fs]],
    colWidths=[0.13] * len(fs),
    rowLabels=['error'],
    colLabels=['order %d' % f.order for f in fs],
    loc='upper right', zorder=100)
table.scale(1, 1.5)
# plt.show()

result = {}
for train, test in KFold(y_lastweek.shape[0], 5):
    # print train.shape, test.shape
    x_train, x_test = x_lastweek[train], x_lastweek[test]
    y_train, y_test = y_lastweek[train], y_lastweek[test]
    for d in [1, 2, 3, 10, 50]:
        if d not in result:
            result[d] = []
        f = gen_model(x_train, y_train, d)
        result[d].append(error(f, x_test, y_test))

for k, v in result.iteritems():
    print k, '\t'.join('%.2e' % x for x in v), '\t%.2e' % np.mean(v)

bestF = gen_model(x_lastweek, y_lastweek, 2) - 10000
roots = fsolve(bestF, x0=800) / (7.0 * 24)
print roots  # [ 5.0628393]

plt.figure()
plt.xticks([w*7*24 for w in range(10)], ['week %d'%w for w in range(10)])
plt.autoscale(tight=True)
plt.grid(True, linestyle='-', color='0.75')
plt.ylim(0, 10000)
plt.plot(fx, bestF(fx), linewidth=4)
plt.show()