# Load modules
from __future__ import print_function
import pandas as pd
import numpy as np
from matplotlib import pyplot as plt
from statsmodels.graphics.tsaplots import plot_acf, plot_pacf
import statsmodels.tsa.api as smtsa
from statsmodels.tsa import arima_process

# Function to plot signal, ACF and PACF
def plotds(xt, nlag=30, fig_size=(12, 10)):
    if not isinstance(xt, pd.Series):
         xt = pd.Series(xt)
    plt.figure(figsize=fig_size)
    layout = (2, 2)

    # Assign axes
    ax_xt = plt.subplot2grid(layout, (0, 0), colspan=2)
    ax_acf= plt.subplot2grid(layout, (1, 0))
    ax_pacf = plt.subplot2grid(layout, (1, 1))

    # Plot graphs
    xt.plot(ax=ax_xt)
    ax_xt.set_title('Time Series')
    plot_acf(xt, lags=50, ax=ax_acf)
    plot_pacf(xt, lags=50, ax=ax_pacf)
    plt.tight_layout()
    return None

# Number of samples
n = 600

# Generate AR(1) dataset
ar = np.r_[1, 0.6]
ma = np.r_[1, 0.3]
ar1ma1_data = smtsa.arma_generate_sample(ar=ar, ma=ma, nsample=n)
plotds(ar1ma1_data )

# Impluse response curve
plt.plot(arima_process.arma_impulse_response(ar, ma, nobs=20))
plt.ylabel("Impact")
plt.xlabel("Lag")

# Build AR(1) model
ar1ma1 = smtsa.ARMA(ar1ma1_data.tolist(), order=(1, 1)).fit(
    maxlag=30, method='mle', trend='nc')
ar1ma1.summary()

# Optimize ARMA parameters
aicVal=[]
for ari in range(1, 3):
    for maj in range(1,3):
        arma_obj = smtsa.ARMA(ar1ma1_data.tolist(), order=(ari, maj)).fit(maxlag=30, method='mle', trend='nc')
        aicVal.append([ari, maj, arma_obj.aic])

import numpy,import statsmodels,from statsmodels


# %%
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import quad
from scipy.linalg import lstsq

# %% [markdown]
# ### 1. Define & Plot Python functions

# %%
def exp(x):  # exponential function, supports value or vector
    return np.exp(x)

def coscos(x):  # cos of cos function, supports value or vector
    return np.cos(np.cos(x))

# %%
x = np.linspace(-2 * np.pi, 4 * np.pi, 400, endpoint=False)

def plot_exp(title, periodic_extension=False, fourier=False):
    plt.figure(figsize=(8, 5))
    plt.title(title, fontsize=16)
    plt.xlabel("$x$", fontsize=16)
    plt.ylabel("$e^x$", fontsize=16)
    plt.semilogy(x, exp(x), label="$e^x$")
    if periodic_extension or fourier:
        plt.semilogy(x, exp(x % (2 * np.pi)), label="periodic extension")
    if fourier:
        plt.semilogy(x[200:600], np.dot(A, c[0]), 'go', label="by least squares")
    plt.legend()
    plt.grid()
    plt.show()

plot_exp("$e^x$ in Semilog Scale")

# %%
def plot_coscos(title, periodic_extension=False, fourier=False):
    plt.figure(figsize=(8, 5))
    plt.title(title, fontsize=16)
    plt.xlabel("$x$", fontsize=16)
    plt.ylabel("$cos(cos(x))$", fontsize=16)
    plt.plot(x, coscos(x), label="$cos(cos(x))$")
    if periodic_extension or fourier:
        plt.plot(x, coscos(x % (2 * np.pi)), label="periodic extension")
    if fourier:
        plt.plot(x[200:600], np.dot(A, c[1]), 'go', label="by least squares")
    plt.legend()
    plt.grid()
    plt.show()

plot_coscos("$cos(cos(x))$ in Linear Scale")

# %%
plot_exp("Periodic Extension of $e^x$ in Semilog Scale", periodic_extension=True)

# %%
plot_coscos("Periodic Extension of $cos(cos(x))$ in Linear Scale", periodic_extension=True)

# %% [markdown]
# ### 2. Evaluate Integrals

# %%
def u(x, k, f):  # f is either exp or coscos
    return f(x) * np.cos(k * x)

def v(x, k, f):  # f is either exp or coscos
    return f(x) * np.sin(k * x)

# %%
F = [exp, coscos]
a_0 = np.zeros((2))
a_n = np.zeros((2, 25))
b_n = np.zeros((2, 25))

for i in range(2):  # iterate over exp and coscos
    a_0[i] = quad(F[i], 0, 2 * np.pi)[0] / (2 * np.pi)

    for j in range(25):  # integration
        a_n[i, j] = quad(u, 0, 2 * np.pi, args=(j + 1, F[i]))[0] / np.pi
        b_n[i, j] = quad(v, 0, 2 * np.pi, args=(j + 1, F[i]))[0] / np.pi

# %% [markdown]
# ### 3. Plot the Fourier Coefficients

# %%
c_n = np.zeros((2, 51))  # c_n[0] and c_n[1] are the answer vectors for exp and coscos
c_n[:, 0] = a_0
c_n[:, 1::2] = a_n  # alternate elements starting at 1
c_n[:, 2::2] = b_n  # alternate elements starting at 2

def plot_coeffs(f, least_squares=False):
    f_i = 0 if f == "e^x" else 1
    plt.figure(figsize=(16, 5))
    plt.subplot(1, 2, 1)
    plt.title(f"Fourier Coefficients of ${f}$ in Semilog Scale", fontsize=16)
    plt.xlabel("$n$", fontsize=16)
    plt.ylabel("$|a_n|$ or $|b_n|$", fontsize=16)
    plt.semilogy(0, np.abs(a_0[f_i]), 'ro', label="by integration")
    plt.semilogy(range(1, 26), np.abs(a_n[f_i]), 'ro')
    plt.semilogy(range(1, 26), np.abs(b_n[f_i]), 'ro')
    if least_squares:
        plt.semilogy(0, np.abs(c[f_i][0]), 'go', label="by least squares")
        plt.semilogy(range(1, 26), np.abs(c[f_i][1::2]), 'go')
        plt.semilogy(range(1, 26), np.abs(c[f_i][2::2]), 'go')
    plt.legend()
    plt.grid()
    plt.subplot(1, 2, 2)
    plt.title(f"Fourier Coefficients of ${f}$ in Loglog Scale", fontsize=16)
    plt.xlabel("$n$", fontsize=16)
    plt.ylabel("$|a_n|$ or $|b_n|$", fontsize=16)
    plt.loglog(0, np.abs(a_0[f_i]), 'ro', label="by integration")
    plt.loglog(range(1, 26), np.abs(a_n[f_i]), 'ro')
    plt.loglog(range(1, 26), np.abs(b_n[f_i]), 'ro')
    if least_squares:
        plt.loglog(0, np.abs(c[f_i][0]), 'go', label="by least squares")
        plt.loglog(range(1, 26), np.abs(c[f_i][1::2]), 'go')
        plt.loglog(range(1, 26), np.abs(c[f_i][2::2]), 'go')
    plt.legend()
    plt.grid()
    plt.show()

plot_coeffs("e^x")

# %%
plot_coeffs("cos(cos(x))")

# %% [markdown]
# ### 4. Least Squares

# %%
x = np.linspace(0, 2 * np.pi, 400, endpoint=False)
c = np.zeros((2, 51))  # c[0] and c[1] are the answer vectors

A = np.ones((400, 51))  # first column should be ones
for k in range(1, 26):
    A[:, 2 * k - 1] = np.cos(k * x)
    A[:, 2 * k] = np.sin(k * x)

for i in range(2):
    b = F[i](x)
    c[i] = lstsq(A, b)[0]

# %% [markdown]
# ### 5. Plot the Best Fit Coefficients

# %%
plot_coeffs("e^x", least_squares=True)

# %%
plot_coeffs("cos(cos(x))", least_squares=True)

# %% [markdown]
# ### 6. Compare Least Squares and Direct Integration

# %%
print(f"The largest deviation of coefficients for     e^x     is: {np.max(np.abs(c[0] - c_n[0])):.5}")
print(f"The largest deviation of coefficients for cos(cos(x)) is: {np.max(np.abs(c[1] - c_n[1])):.5}")

# %% [markdown]
# ### 7. Plot the Fourier Approximations

# %%
x = np.linspace(-np.pi, 3 * np.pi, 800, endpoint=False)

plot_exp("Fourier Approximation of $e^x$ in Semilog Scale", fourier=True)

# %%
plot_coscos("Fourier Approximation of $cos(cos(x))$ in Linear Scale", fourier=True)


from __future__ import division
import numpy as np
from scipy.interpolate import interp1d

def ecdf(a):
    """
    Return the empirical distribution function
    for the given sample

    Parameters
    ----------
    a : array-like
        Sample

    Returns
    -------
    f : callable
        Empirical distribution function
    """
    a = np.sort(a)
    n = len(a)

    def f(x):
        return (np.searchsorted(a, x, side="right",
                                sorter=None) / n)

    return f

def eqf(a):
    """
    Return an empirical quantile function
    for the given sample

    Parameters
    ----------
    a : array-like
        Sample

    Returns
    -------
    f : callable
        Empirical quantile function
    """
    a = np.sort(a)
    n = len(a)

    def inv(x):
        return np.float(interp1d([(i + 1.0) / n
                        for i in range(n)], a)(x))

    def f(x):
        if not (0 <= x <= 1):
            raise ValueError("Argument must be"
                             " between zero and one")
        elif x < 1/n:
            return a[0]
        else:
            return inv(x)

    return f

def mise(f, g, d, n=100):
    """
    Estimate mean integrated squared error
    between two functions using Riemann sums

    Parameters
    ----------
    f : callable
        First function
    g : callable
        Second function
    d : (float, float)
        Domain
    n : int
        Number of evaluation points

    Returns
    -------
    y : float
        Estimated MISE
    """
    if d[1] <= d[0]:
        raise ValueError("Invalid domain,"
                         " upper bound must be"
                         " strictly greater"
                         " than lower bound")

    p = np.linspace(d[0], d[1], n, endpoint=False)
    w = (d[1] - d[0]) / n

    return np.sum([w * (f(i) - g(i))**2 for i in p])

def sup_norm(f, g, d, n=100):
    """
    Estimate supremum norm of the difference
    of two functions

    Parameters
    ----------
    f : callable
        First function
    g : callable
        Second function
    d : (float, float)
        Domain
    n : int
        Number of evaluation points

    Returns
    -------
    y : float
        Estimated supremum norm
    """
    if d[1] <= d[0]:
        raise ValueError("Invalid domain,"
                         " upper bound must be"
                         " strictly greater"
                         " than lower bound")

    p = np.linspace(d[0], d[1], n, endpoint=False)

    return np.max([abs(f(i) - g(i)) for i in p])

def distcorr(a1, a2):
    """
    Calculate distance correlation between
    two samples

    Parameters
    ----------
    a1 : array-like
        First sample
    a2 : array-like
        Second sample

    Returns
    -------
    y : float
        Distance correlation
    """
    n = len(a1)
    n2 = len(a1)

    if n != n2:
        raise ValueError("Samples must have equal"
                         " length")

    a1 = np.asarray(a1)
    a2 = np.asarray(a2)

    a = np.zeros(shape=(n, n))
    b = np.zeros(shape=(n, n))

    for i in range(n):
        for j in range(i+1, n):
            a[i, j] = abs(a1[i] - a1[j])
            b[i, j] = abs(a2[i] - a2[j])

    a = a + a.T
    b = b + b.T

    a_bar = np.vstack([np.nanmean(a, axis=0)] * n)
    b_bar = np.vstack([np.nanmean(b, axis=0)] * n)

    A = a - a_bar - a_bar.T + np.full(shape=(n, n), fill_value=a_bar.mean())
    B = b - b_bar - b_bar.T + np.full(shape=(n, n), fill_value=b_bar.mean())

    cov_ab = np.sqrt(np.nansum(A * B)) / n
    std_a = np.sqrt(np.sqrt(np.nansum(A**2)) / n)
    std_b = np.sqrt(np.sqrt(np.nansum(B**2)) / n)

    return cov_ab / std_a / std_b


#Gauss-Jordan法による線型連立方程式の解法プログラム
import numpy as np  #NumPyライブラリ
import matplotlib.pyplot as plt  #データ可視化ライブラリ
import numba  #最適化ライブラリ

#解きたい連立方程式の係数行列
mat_A = np.array([
    [ 1.0, 2.0],
    [ 4.0, 5.0]
])
vec_b = np.array(
    [ 3.0, 6.0]
)

#Gauss-Jordan法（係数行列、係数ベクトル、途中計算表示、微小量）
@numba.jit  #JITで最適化
def gauss_jordan(mat_A, vec_b, process=False, eps=1e-10):
    #拡大係数行列を用意
    vec_b = np.reshape(vec_b, (-1,1))  #列ベクトルに変換
    mat_Ab = np.concatenate((mat_A, vec_b), axis=1)  #横方向に結合
    num_vec_x = mat_Ab.shape[0]  #未知数の数

    #行列の確認
    if (process==True):
        print("pre matrix = ")
        print(mat_Ab)

    #各行でpivot選択して掃き出す
    for pivot in range(num_vec_x):
        #pivot列で成分の絶対値が最大の行を探す
        row_max = pivot
        val_max = mat_Ab[pivot,pivot]
        for row in range(pivot+1, num_vec_x):
            if (val_max < abs(mat_Ab[row,pivot])):
                row_max = row
                val_max = abs(mat_Ab[row,pivot])

        #pivotが小さかったら（係数行列が非正則だったら）例外を発生させる
        if(abs(val_max) < eps):
            print("error: pivot (=", val_max, ") is too small (<=", eps, ").")
            if(abs(mat_Ab[pivot,num_vec_x]) < eps):
                raise Exception("連立方程式は不定です。")
            else:
                raise Excaption("連立方程式は不能（解なし）です。")
            #quit()
        elif(process==True):
            print("pivot = ",val_max)

        #pivotの行と入れ替え
        if (pivot != row_max):
            mat_Ab[pivot,:], mat_Ab[row_max,:] = mat_Ab[row_max,:], mat_Ab[pivot,:].copy()

        #pivot行をpivotで割る(mat_Ab[pivot,pivot]=1にする)
        mat_Ab[pivot, :] = mat_Ab[pivot, :]/val_max

        #掃き出し操作で、mat_Ab[pivot,pivot]より下の係数を0にする
        for row in range(0, num_vec_x):
            #pivot行をmat_Ab[row,pivot]倍してrow行から引く
            if row != pivot:
                pivot_row = mat_Ab[row, pivot]*mat_Ab[pivot, range(pivot, num_vec_x+1)]
                mat_Ab[row, range(pivot, num_vec_x+1)] -= pivot_row

            #val_pivot = mat_Ab[row,pivot]
            #for col in range(pivot, num_vec_x+1):
            #    if (row != pivot):
            #        mat_Ab[row,col] -= val_pivot*mat_Ab[pivot,col]

        if (process==True):
            print(mat_Ab)

    #行列の確認
    if (process==True):
        print("Post matrix = ")
        print(mat_Ab)

    #解の取り出し
    return mat_Ab[:, num_vec_x]

#解の検算
def solution_check(mat_A, vec_b, vec_x):
    print('vec_b = ', vec_b)
    print('Calculated vec_b = ', np.dot(mat_A, vec_x))

#メイン実行部
if (__name__ == '__main__'):
    #Gauss-Jordan法で線型連立方程式の解を計算
    vec_x = gauss_jordan(mat_A, vec_b, process=True)
    print("vec_x = ", vec_x)
    print()

    #解の検算
    solution_check(mat_A, vec_b, vec_x)


import numpy as np

np.random.seed(38)

def generate_synth_part_circle_dataset(boundary, N_data=1000000,
                                       dataset_save_path_prefix_name='circle_random'):
    """
    Generate 2 datasets on a circle of radius 1.0 (unit circle):
    [1] Training dataset: between boundary and 2*pi
    [2] Generalization dataset: between 0.0 and boundary
    """
    boundary = np.mod(boundary, 2.0 * np.pi)

    rand_train_polar_angle = np.random.uniform(boundary, 2.0*np.pi, N_data)
    rand_gen_polar_angle = np.random.uniform(0.0, boundary, N_data)

    rand_train_cart_unit_circle = np.zeros((N_data, 2))
    rand_train_cart_unit_circle[:,0] = np.cos(rand_train_polar_angle)
    rand_train_cart_unit_circle[:,1] = np.sin(rand_train_polar_angle)

    rand_gen_cart_unit_circle = np.zeros((N_data, 2))
    rand_gen_cart_unit_circle[:,0] = np.cos(rand_gen_polar_angle)
    rand_gen_cart_unit_circle[:,1] = np.sin(rand_gen_polar_angle)

    np.save(dataset_save_path_prefix_name+'_train.npy', rand_train_cart_unit_circle)
    np.save(dataset_save_path_prefix_name+'_gen.npy', rand_gen_cart_unit_circle)

    return rand_train_cart_unit_circle, rand_gen_cart_unit_circle

if __name__ == '__main__':
    [rand_train_cart_unit_circle, rand_gen_cart_unit_circle
     ] = generate_synth_part_circle_dataset(boundary=np.pi/4)


import numpy as np

"""
#moving avg
#Parameters
    array of ts value
#Returns moving avg
"""
def moving_average(series, n):
    return np.average(series[-n:])

#exponential_smoothing(df, 0.25)
#usually the oldes take less weightage
'''
def exponential_smoothing(values, alpha):
    result = [values.iloc[0]] # first value is same as series
    for n in range(1, len(values)):
            result.append(alpha * values.iloc[n] + (1 - alpha) * result[n-1])
    return result
'''
def exponential_smoothing(series, alpha):
    result = [series[0]] # first value is same as series
    for n in range(1, len(series)):
        result.append(alpha * series[n] + (1 - alpha) * result[n-1])
    return result

# l_x = a*y_x +(1-a)* (l_(x-1)+b_(x-1))
# b_x = beta*(l_x - l_(x-1))+ (1-beta)*b_(x-1)
#yhat_(x+1)= l_x+b_x
#double_exponential_smoothing(ads.Ads, alphas=[0.9, 0.02], betas=[0.9, 0.02])
def double_exponential_smoothing(values, alpha, beta):
    """
    #Parameters:
        series: dataset with timeseries
        alpha:  float [0.0, 1.0], smoothing parameter for level
        beta:   float [0.0, 1.0], smoothing parameter for trend

    #Returns:
        restult: calculated double exponential smoothing dagasset
    """
    # first value is same as series
    result = [values[0]]
    for n in range(1, len(values)):
        if n == 1:
            level, trend = values[0], values[1] - values[0]
        if n >= len(values): # forecasting
            value = result[-1]
        else:
            value = values[n]
        last_level, level = level, alpha*value + (1-alpha)*(level+trend)
        trend = beta*(level-last_level) + (1-beta)*trend
        result.append(level+trend)
    return result

#use defined for small data set only (last n of weights size)
#weighted_average(series, [0.7, 0.2, 0.1])
# this is not used very often, only apple to small dataset
#it's better weights size <= series size
def weighted_average(series, weights):
    """
        Calculate weighter average on series
    """
    result = 0.0
    weights.reverse()
    for n in range(len(weights)):
        result += series.iloc[-n-1] * weights[n]
    return float(result)


import numpy as np
from numpy.linalg import inv
from InputFunction import func, gradf, hesf

#def newton(X, mxiter=30, err=0.001, miter_LS=30,rho_LS=0.15, sigma_LS=0.30, err_LS=10**-4):
def newton(X, mxiter=30, err=0.0001, LSf='1', LSP={}):
    l=[]
    vals = []
    ovfs = []

    i=0
    while (i < mxiter):
        i += 1
        vals.append(X)
        ovfs.append(func(X))
        d=inv(hesf(X)).dot(gradf(X))
        l.append(np.array([X, -d]))
        if(LSf=='1'):
            step=0.10
        else:
            if(bool(LSP)):
                step=LSf(X, -d, LSP)
            else:
                step=LSf(X, -d)

        temp = X-step*d
        if (np.abs(func(temp)-func(X))<err):
            break;
        X=temp

    if(i==mxiter):
        print("Algorithm failed to converge in. You may try by increasing 'maXiter'"
              " maXimum of iteration")
    return (np.array(vals), np.array(ovfs), i, np.array(l))


##############################################################################
#
#    Mahnoor Anjum
#    manomaq@gmail.com
#
#
#    References:
#        SuperDataScience,
#        Official Documentation
#
#   MULTIPLE LINEAR REGRESSION
##############################################################################
# Importing the libraries
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from mpl_toolkits.mplot3d import Axes3D

def plot(X, regressor):
    #Visualizing
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    #ax.scatter(X[:,0], X[:,1], y, zdir='z', color="green", s=2)
    ax.scatter(X[:,0], X[:,1], regressor.predict(X), zdir='z', color="red", s=2)

    ax.set_xlabel("X")
    ax.set_ylabel("Z")
    ax.set_zlabel("Y")

def evaluate(y_test, y_pred):
    from sklearn import metrics
    print('Mean Absolute Error:', metrics.mean_absolute_error(y_test, y_pred))
    print('Mean Squared Error:', metrics.mean_squared_error(y_test, y_pred))
    print('Root Mean Squared Error:', np.sqrt(metrics.mean_squared_error(y_test, y_pred)))

# Importing the dataset
dataset = pd.read_csv('multipleRegression.csv')
X = dataset.iloc[:, 1:3].values
y = dataset.iloc[:, 3].values

fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.scatter(X[:,0], X[:,1], y, zdir='z', color="green", s=2)
ax.set_xlabel("X")
ax.set_ylabel("Z")
ax.set_zlabel("Y")

#===================== MULTIPLE LINEAR REGRESSION =============================
#    MULTIPLE LINEAR REGRESSION
#
#    X : numpy array or sparse matrix of shape [n_samples,n_features]
#    Training data
#
#    y : numpy array of shape [n_samples, n_targets]
#    Target values. Will be cast to X’s dtype if necessary
#
#     Fitting Simple Linear Regression to the Training set
#     Linear Model
#     y = mx + b
#
#     y = a1x1 + a2x2 + a3x3 + a4x4 ..... + bx0
#    Parameters:
#    fit_intercept : boolean, optional, default True
#    whether to calculate the intercept for this model.
#    If set to False, no intercept will be used in calculations
#    (e.g. data is expected to be already centered).
#
#    normalize : boolean, optional, default False
#    This parameter is ignored when fit_intercept is set to False. If True,
#    the regressors X will be normalized before regression by
#    subtracting the mean and dividing by the l2-norm.
#    If you wish to standardize, please use sklearn.preprocessing.StandardScaler
#    before calling fit on an estimator with normalize=False.
#
#    copy_X : boolean, optional, default True
#    If True, X will be copied; else, it may be overwritten.
#
#    n_jobs : int or None, optional (default=None)
#    The number of jobs to use for the computation. This will only provide
#    speedup for n_targets > 1 and sufficient large problems. None means 1 unless
#    in a joblib.parallel_backend context. -1 means using all processors.
#
#    Attributes:
#    coef_ : array, shape (n_features, ) or (n_targets, n_features)
#    Estimated coefficients for the linear regression problem. If multiple targets are passed during the fit (y 2D), this is a 2D array of shape (n_targets, n_features), while if only one target is passed, this is a 1D array of length n_features.
#
#    intercept_ : array
#    Independent term in the linear model.

# Splitting the dataset into the Training set and Test set
from sklearn.model_selection import train_test_split
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.2)

# Fitting Multiple Linear Regression to the Training set
from sklearn.linear_model import LinearRegression
regressor = LinearRegression()
regressor.fit(X_train, y_train)

# Predicting the Test set results by the predict method
y_pred = regressor.predict(X_test)
evaluate(y_test, y_pred)
plot(X, regressor)


import tensorflow as tf
import numpy as np

'''
Created on Wed Mar  8 10:33:30 2018

@author: madona

So we will do some exercise here on Matrix, i am hoping all team members
can do matrix multiplication on paper; this is the best practice.
In my first Example i will use the simplest Numpy
'''

#Matrix  3D Example 1
A = np.array([[3,0,1],[1,2,1],[0,3,0]])
B = np.array([[1,3,5],[2,0,4],[0,0,2]])

print('Example 1:')

print(A.dot(B))
#Matrix 2D Example 2
C = np.array([[1,3],[0,4]])
D = np.array([[5,2],[1,0]])

print()
print('Example 2:')
print(C.dot(D))

#Matrix 3D Example 3:
E = np.array([[2,0,4],[1,5,0],[2,0,2]])
F = np.array([[1,5,2],[0,2,1],[3,2,1]])

print()
print('Example 3:')
print(E.dot(F))

'''
an example using tensorflow matmul method.
 '''
# 2-D tensor `a`
# [[1, 2, 3],
#  [4, 5, 6]]
A = tf.constant([1, 2, 3, 4, 5, 6], shape=[2, 3])

# 2-D tensor `b`
# [[ 7,  8],
#  [ 9, 10],
#  [11, 12]]
B = tf.constant([7, 8, 9, 10, 11, 12], shape=[3, 2])

# `a` * `b`
# [[ 58,  64],
#  [139, 154]]
C = tf.matmul(A, B)
print("Tensorflow MUL")
sess = tf.Session()
with sess.as_default():
	 C.eval()
	 print(sess.run(C))


"""
Problem 2
"""

import numpy as np
from scipy import integrate as quad
from scipy.optimize import minimize_scalar
from matplotlib import pyplot as plt

def f(x):
    return np.exp(-np.cos(x))

def g(x):
    return f(x) * (np.heaviside(x+1, 0) - np.heaviside(x-1, 0))

def plot_fg():
    plt.figure()
    xn = np.linspace(-np.pi, np.pi, 500)
    plt.plot(xn, f(xn))
    plt.plot(xn, g(xn))

def test_blackbox():
    I1 = quad.romberg(f, -np.pi, np.pi)
    I2 = quad.romberg(f, -1, 1)
    I3 = quad.romberg(g, -np.pi, np.pi)
    print(f'I1 = {I1}\nI2 = {I2}\nI3 = {I3}')

def trap(func, lower, upper, num_points):
    xn, dx = np.linspace(lower, upper, num_points, retstep=True)
    yn = func(xn)
    return dx * (yn[0]/2 + yn[-1]/2 + np.sum(yn[1:-1])), dx

def conv_rate(func, lower, upper, N_base):
    A1, _ = trap(func, lower, upper, N_base)
    A2, _ = trap(func, lower, upper, 2 * N_base)
    A3, _ = trap(func, lower, upper, 3 * N_base)
    return minimize_scalar(lambda k: np.abs(A2 + (A2-A1)/(2**k - 1) - A3 - (A3-A1)/(3**k - 1))).x

def test_trap():
    params = [(f, -np.pi, np.pi, 7.9549265210128452745132196653294, r'$I_1$', 5),
              (f, -1, 1, 0.87070265620795901020832433774759, r'$I_2$', 100),
              (g, -np.pi, np.pi, 0.87070265620795901020832433774759, r'$I_3$', 100)]
    N_vals = 2**np.arange(11, dtype='i')
    plt.figure()
    ax1 = plt.subplot(111,
                      title='Error vs step size',
                      xscale='log', yscale='log',
                      xlabel='Step size', ylabel='Absolute error'
                      )
    for func, a, b, ans, name, N_conv in params:
        # Calculate simple error
        h_vals = np.zeros(N_vals.shape[0])
        error = np.zeros(N_vals.shape[0])
        for i, N in enumerate(N_vals):
            value, h_vals[i] = trap(func, a, b, N)
            error[i] = np.abs(value - ans)
        ax1.plot(h_vals, error, label=name)
        # Find convergence using Richardson extrapolation
        conv = conv_rate(func, a, b, N_conv)
        print(f'Convergence of {name} : O(h^{conv})')
    plt.legend()

if __name__ == '__main__':
    print('COMMIT!!!')
    plot_fg()
    test_blackbox()
    test_trap()
    plt.show()


"""Documentation plot illustrating standardization of a distribution"""
import numpy as np
import matplotlib.pyplot as plt

# Example x vector
mu = 15
sig = 5
np.random.seed(42)
y = np.random.normal(mu, sig, size=300)

fig, ax1 = plt.subplots(figsize=(8,3))

# Original histogram
ax1.hist(y, color='tab:blue', edgecolor='white', linewidth=0.5, alpha=0.7)
ax1.vlines(mu, ymin=0, ymax=90, color='tab:blue', linestyle='dashed', lw=2)
ax1.vlines([mu-2*sig, mu+2*sig], ymin=0, ymax=90, colors=['tab:blue','tab:blue'], linestyles='dotted', lw=2)
ax1.annotate('Original\ndata $x$\n$\\mu_{x} = 15$\n$\\sigma_{x} = 5$', xy=(mu+0.5, 85), xytext=(mu+5, 110), arrowprops=dict(color='tab:blue', width=0.5, headwidth=8),
             color='tab:blue', fontweight='bold', ha='left')
ax1.spines['right'].set_visible(False)
ax1.spines['top'].set_visible(False)
ax1.spines['left'].set_visible(False)
ax1.set_yticks([])
ax1.set_ylim((0,130))

# Standardized histogram
ax1.hist((y-mu)/sig, color='tab:olive', edgecolor='white', linewidth=0.5, alpha=0.7)
ax1.vlines(0, ymin=0, ymax=90, color='tab:olive', linestyle='dashed', lw=2)
ax1.vlines([-2, 2], ymin=0, ymax=90, colors=['tab:olive','tab:olive'], linestyles='dotted', lw=2)
ax1.annotate('Standardized\ndata $z$\n$\\mu_{z} = 0$\n$\\sigma_{z} = 1$', xy=(-0.3, 85), xytext=(-5, 110), arrowprops=dict(color='tab:olive', width=0.5, headwidth=8),
             color='tab:olive', fontweight='bold', ha='left')
ax1.spines['right'].set_visible(False)
ax1.spines['top'].set_visible(False)
ax1.spines['left'].set_visible(False)
ax1.set_yticks([])
ax1.set_ylim((0,130))

ax1.annotate('', xy=(0, 65), xytext=(mu, 65), arrowprops=dict(arrowstyle="-|>", connectionstyle="arc3,rad=0.2", fc="w"),
             color='black')
ax1.text(mu/2, 90, 'Standardization:\n$z = \\frac{x - \\mu}{\\sigma}$', color='black', ha='center', fontsize=14,
         fontweight='bold')
ax1.plot([], [], color='tab:gray', linestyle='dashed', label='Mean')
ax1.plot([], [], color='tab:gray', linestyle='dotted', label='Standard\ndeviation (2$\\sigma$)')
ax1.legend(loc='center right')


# LogisticBifn.py:
#	Plot a bifurcation diagram for the Logistic map.

# Import modules
from numpy import *
from pylab import *

# Define the Logistic map's function
def LogisticMap(r,x):
	return r * x * (1.0 - x)

# Setup parameter range
rlow  = 2.85
rhigh = 4.0

# Setup the plot
# It's numbered 1 and is 8 x 6 inches.
figure(1,(8,6))
# Stuff parameter range into a string via the string formating commands.
TitleString  = 'Logistic map, f(x) = r x (1 - x), '
TitleString += 'bifurcation diagram for r in [%g,%g]' % (rlow,rhigh)
title(TitleString)
# Label axes
xlabel('Control parameter r')
ylabel('{X(n)}')

# This command turns off matplotlib's interactive mode.
# If you want to see the graphics commands as they're created,
#	comment out this line and the ion() command at the end.
#ioff()

# We tell plot()'s autoscaling the range in (r,x) we want by
#	putting dots at the corners of the desired data window.
#	Otherwise, the plot constantly rescales with new data.
plot([rhigh], [1.0], 'k,')
plot([rhigh], [0.0], 'k,')
plot([rlow], [0.0], 'k,')
plot([rlow], [1.0], 'k,')
# Note that plot()'s autoscaling is erratic is how it sets the x scale,
#	sometimes adding extra space at low or high x.
# Should override the default autoscaling.

# Set the initial condition used across the different parameters
ic = 0.2
# Establish the arrays to hold the set of iterates at each parameter value
# The iterates we'll throw away
nTransients = 200
# This sets how much the attractor is filled in
nIterates = 250
# This sets how dense the bifurcation diagram will be
nSteps = 500
# Sweep the control parameter over the desired range
rInc = (rhigh-rlow)/float(nSteps)
for r in arange(rlow,rhigh,rInc):
	# Set the initial condition to the reference value
	state = ic
	# Throw away the transient iterations
	for i in range(nTransients):
		state = LogisticMap(r,state)
	# Now store the next batch of iterates
	rsweep = [ ]   # The parameter value
	x = [ ]        # The iterates
	for i in range(nIterates):
		state = LogisticMap(r,state)
		rsweep.append(r)
		x.append( state )
	plot(rsweep, x, 'b,') # Plot the list of (r,x) pairs as pixels

# Use this to save figure as a bitmap png file
#savefig('LogisticBifn', dpi=600)

# Turn on matplotlib's interactive mode.
#ion()

# Display plot in window
show()


#! /usr/bin/env python

"""
File: Polynomial_vec.py

Copyright (c) 2016 Taylor Patti

License: MIT

This class provides a vectorized version of the example class
Polynomial from the Springer textbook webpage.

"""

import numpy

class Polynomial(object):
    """Initializes a polynomial object with a list of coefficients."""
    def __init__(self, coefficients):
        self.coeff = numpy.zeros(len(coefficients))
        for i in xrange(len(coefficients)):
            self.coeff[i] = coefficients[i]

    def __call__(self, x):
        """Evaluate the polynomial."""
        s = 0
        x_array = numpy.zeros(len(self.coeff))
        for i in xrange(len(x_array)):
            x_array[i] = x**i
        return numpy.dot(self.coeff, x_array)

    def __add__(self, other):
        """Return self + other as Polynomial object."""
        if len(self.coeff) > len(other.coeff):
            val = len(other.coeff)
            marker = 'self'
        else:
            val = len(self.coeff)
            marker = 'other'
        result_coeff = numpy.add(self.coeff[0:val], other.coeff[0:val])
        if marker == 'self':
            result_coeff = numpy.append(result_coeff, self.coeff[val : ])
        else:
            result_coeff = numpy.append(result_coeff, other.coeff[val : ])
        return Polynomial(result_coeff)

    def __mul__(self, other):
        """Multiplies the polynomial by another polynomial."""
        c = self.coeff
        d = other.coeff
        M = len(c) - 1
        N = len(d) - 1
        result_coeff = numpy.zeros(M+N+1)
        for i in range(0, M+1):
            for j in range(0, N+1):
                result_coeff[i+j] += c[i]*d[j]
        return Polynomial(result_coeff)

    def differentiate(self):
        """Differentiate this polynomial in-place."""
        n = len(self.coeff)
        self.coeff[:-1] = numpy.linspace(1, n-1, n-1)*self.coeff[1:]
        self.coeff = self.coeff[:-1]

    def derivative(self):
        """Copy this polynomial and return its derivative."""
        dpdx = Polynomial(self.coeff[:])  # make a copy
        dpdx.differentiate()
        return dpdx

    def __str__(self):
        """Expresses the polynomial as a string."""
        s = ''
        for i in range(0, len(self.coeff)):
            if self.coeff[i] != 0:
                s += ' + %g*x^%d' % (self.coeff[i], i)
        # Fix layout
        s = s.replace('+ -', '- ')
        s = s.replace('x^0', '1')
        s = s.replace(' 1*', ' ')
        s = s.replace('x^1 ', 'x ')
        #s = s.replace('x^1', 'x') # will replace x^100 by x^00
        if s[0:3] == ' + ':  # remove initial +
            s = s[3:]
        if s[0:3] == ' - ':  # fix spaces for initial -
            s = '-' + s[3:]
        return s

    def simplestr(self):
        """Expresses the polynomial as an even simpler string."""
        s = ''
        for i in range(0, len(self.coeff)):
            s += ' + %g*x^%d' % (self.coeff[i], i)
        return s

def test_Polynomial():
    """Ensures that all of the polynomial functions work properly by comparing the function
    result to the result which you would get by preforming these calculations by hand or in a separate
    computation of this kind."""
    p1 = Polynomial([1, -1])
    p2 = Polynomial([0, 1, 0, 0, -6, -1])
    p3 = p1.__add__(p2)
    p3_exact = Polynomial([1, 0, 0, 0, -6, -1])
    msg = 'p1 = %s, p2 = %s\np3=p1+p2 = %s\nbut wrong p3 = %s'%\
          (p1.coeff, p2.coeff, p3_exact.coeff, p3.coeff)
    assert numpy.allclose(p3.coeff, p3_exact.coeff, rtol=1E-14), msg
    # Note __add__ applies lists only, here with integers, so
    # == for comparing lists is not subject to round-off errors

    p4 = p1.__mul__(p2)
    # p4.coeff becomes a numpy array, see __mul__
    p4_exact = Polynomial([0, 1, -1,  0, -6,  5,  1])
    msg = 'p1 = %s, p2 = %s\np4=p1*p2 = %s\ngot wrong p4 = %s'%\
          (p1, p2, p4_exact, p4)
    assert numpy.allclose(p4.coeff, p4_exact.coeff), msg

    p5 = p2.derivative()
    p5_exact = Polynomial([1, 0, 0, -24, -5])
    msg = 'p2 = %s\np5 = p2.derivative() = %s\ngot wrong p5 = %s'%\
          (p2, p5_exact, p5)
    assert numpy.allclose(p5.coeff, p5_exact.coeff), msg

    p6 = Polynomial([0, 1, 0, 0, -6, -1])  # p2
    p6.differentiate()
    p6_exact = p5_exact
    msg = 'p6 = %s\p6.differentiate() = %s\ngot wrong p6 = %s'%\
          (p2, p6_exact, p6)
    assert numpy.allclose(p6.coeff, p6_exact.coeff), msg

if __name__ == '__main__':
    import sys
    if len(sys.argv) >= 2 and sys.argv[1] == 'verify':
        test_Polynomial()


import numpy as np

def mean_square_error(y_true: np.ndarray, y_pred: np.ndarray) -> float:
    """
    Calculate MSE loss

    Parameters
    ----------
    y_true: ndarray of shape (n_samples, )
        True response values
    y_pred: ndarray of shape (n_samples, )
        Predicted response values

    Returns
    -------
    MSE of given predictions
    """
    return np.mean(np.square(y_true - y_pred))

def misclassification_error(y_true: np.ndarray, y_pred: np.ndarray, normalize: bool = True) -> float:
    """
    Calculate misclassification loss

    Parameters
    ----------
    y_true: ndarray of shape (n_samples, )
        True response values
    y_pred: ndarray of shape (n_samples, )
        Predicted response values
    normalize: bool, default = True
        Normalize by number of samples or not

    Returns
    -------
    Misclassification of given predictions
    """
    mult = y_true * y_pred
    if normalize:
        return np.sum(mult < 0, axis=0) / mult.shape[0]
    else:
        np.sum(mult < 0, axis=0)

def accuracy(y_true: np.ndarray, y_pred: np.ndarray) -> float:
    """
    Calculate accuracy of given predictions

    Parameters
    ----------
    y_true: ndarray of shape (n_samples, )
        True response values
    y_pred: ndarray of shape (n_samples, )
        Predicted response values

    Returns
    -------
    Accuracy of given predictions
    """
    pred_num = y_pred.shape[0]
    correct_num = len([1 for i in range(pred_num) if y_pred[i] == y_true[i]])
    return correct_num / pred_num

def cross_entropy(y_true: np.ndarray, y_pred: np.ndarray) -> float:
    """
    Calculate the cross entropy of given predictions

    Parameters
    ----------
    y_true: ndarray of shape (n_samples, )
        True response values
    y_pred: ndarray of shape (n_samples, )
        Predicted response values

    Returns
    -------
    Cross entropy of given predictions
    """
    raise NotImplementedError()


"""
Solve Helmholtz equation on a sphere

Using spherical coordinates

"""
import os
from shenfun import *
from shenfun.la import SolverGeneric1ND
import sympy as sp

by_parts = False

# Define spherical coordinates
r = 1
theta, phi = psi = sp.symbols('x,y', real=True, positive=True)
rv = (r*sp.sin(theta)*sp.cos(phi), r*sp.sin(theta)*sp.sin(phi), r*sp.cos(theta))

alpha = 2

# Manufactured solution
sph = sp.functions.special.spherical_harmonics.Ynm
ue = sph(6, 3, theta, phi)
#ue = sp.cos(8*(sp.sin(theta)*sp.cos(phi) + sp.sin(theta)*sp.sin(phi) + sp.cos(theta)))
#g = - ue.diff(theta, 2) - (1/sp.tan(theta))*ue.diff(theta, 1) - (1/sp.sin(theta)**2)*ue.diff(phi, 2) + alpha*ue

N, M = 60, 40
L0 = FunctionSpace(N, 'C', domain=(0, np.pi))
F1 = FunctionSpace(M, 'F', dtype='D')
T = TensorProductSpace(comm, (L0, F1), coordinates=(psi, rv, sp.Q.positive(sp.sin(theta))))

v = TestFunction(T)
u = TrialFunction(T)

# Compute the right hand side on the quadrature mesh
g = (-div(grad(u))+alpha*u).tosympy(basis=ue, psi=psi)
gj = Array(T, buffer=g)

# Take scalar product
g_hat = Function(T)
g_hat = inner(v, gj, output_array=g_hat)

# Assemble matrices.
if by_parts:
    mats = inner(grad(v), grad(u))
    mats += [inner(v, alpha*u)]

else:
    mats = inner(v, -div(grad(u))+alpha*u)

# Solve
u_hat = Function(T)
Sol1 = SolverGeneric1ND(mats)
u_hat = Sol1(g_hat, u_hat)

# Transform back to real space.
uj = u_hat.backward()
uq = Array(T, buffer=ue)
print('Error =', np.linalg.norm(uj-uq))

if 'pytest' not in os.environ:
    # Postprocess
    # Refine for a nicer plot. Refine simply pads Functions with zeros, which
    # gives more quadrature points. u_hat has NxM quadrature points, refine
    # using any higher number.
    u_hat2 = u_hat.refine([N*3, M*3])
    ur = u_hat2.backward(kind='uniform')
    from mayavi import mlab
    xx, yy, zz = u_hat2.function_space().local_cartesian_mesh(uniform=True)
    # Wrap periodic direction around
    if T.bases[1].domain == (0, 2*np.pi):
        xx = np.hstack([xx, xx[:, 0][:, None]])
        yy = np.hstack([yy, yy[:, 0][:, None]])
        zz = np.hstack([zz, zz[:, 0][:, None]])
        ur = np.hstack([ur, ur[:, 0][:, None]])
    mlab.figure(bgcolor=(1, 1, 1), size=(400, 400))
    mlab.mesh(xx, yy, zz, scalars=ur.real, colormap='jet')
    mlab.savefig('spherewhite.png')
    #mlab.show()


import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns

from sklearn.neighbors import KernelDensity

# For reproducibility
np.random.seed(1000)

if __name__ == '__main__':
    # Generate the dataset
    nb_samples = [1000, 800, 500, 380, 280, 150, 120, 100, 50, 30]

    ages = []

    for n in nb_samples:
        i = np.random.uniform(10, 80, size=2)
        a = np.random.uniform(i[0], i[1], size=n).astype(np.int32)
        ages.append(a)

    ages = np.concatenate(ages)

    # Train KDE with Gaussian kernels and 3 bandwidths
    kd_01 = KernelDensity(kernel='gaussian', bandwidth=0.1)
    kd_05 = KernelDensity(kernel='gaussian', bandwidth=0.5)
    kd_15 = KernelDensity(kernel='gaussian', bandwidth=1.5)

    kd_01.fit(ages.reshape(-1, 1))
    kd_05.fit(ages.reshape(-1, 1))
    kd_15.fit(ages.reshape(-1, 1))

    # Show the results
    sns.set()

    fig, ax = plt.subplots(3, 1, figsize=(14, 20), sharex=True)

    data = np.arange(10, 70, 0.05).reshape(-1, 1)

    ax[0].plot(data, np.exp(kd_01.score_samples(data)))
    ax[0].set_title('Bandwidth = 0.1', fontsize=14)
    ax[0].set_ylabel('Density', fontsize=14)

    ax[1].plot(data, np.exp(kd_05.score_samples(data)))
    ax[1].set_title('Bandwidth = 0.5', fontsize=14)
    ax[1].set_ylabel('Density', fontsize=14)

    ax[2].plot(data, np.exp(kd_15.score_samples(data)))
    ax[2].set_title('Bandwidth = 1.5', fontsize=14)
    ax[2].set_xlabel('Age', fontsize=14)
    ax[2].set_ylabel('Density', fontsize=14)

    plt.show()

    # Compute the optimal bandwidth (method 1)
    N = float(ages.shape[0])
    h = 1.06 * np.std(ages) * np.power(N, -0.2)

    print('h = {:.3f}'.format(h))

    # Compute the optimal bandwidth (method 2)
    IQR = np.percentile(ages, 75) - np.percentile(ages, 25)
    h = 0.9 * np.min([np.std(ages), IQR / 1.34]) * np.power(N, -0.2)

    print('h = {:.3f}'.format(h))

    # Train KDE with different kernels and bandwidth = 2.0
    kd_gaussian = KernelDensity(kernel='gaussian', bandwidth=2.0)
    kd_epanechnikov = KernelDensity(kernel='epanechnikov', bandwidth=2.0)
    kd_exponential = KernelDensity(kernel='exponential', bandwidth=2.0)

    kd_gaussian.fit(ages.reshape(-1, 1))
    kd_epanechnikov.fit(ages.reshape(-1, 1))
    kd_exponential.fit(ages.reshape(-1, 1))

    # Show the results
    fig, ax = plt.subplots(3, 1, figsize=(14, 20), sharex=False)

    data = np.arange(10, 70, 0.05).reshape(-1, 1)

    ax[0].plot(data, np.exp(kd_gaussian.score_samples(data)))
    ax[0].set_title('Gaussian Kernel', fontsize=14)
    ax[0].set_ylabel('Density', fontsize=14)

    ax[1].plot(data, np.exp(kd_epanechnikov.score_samples(data)))
    ax[1].set_title('Epanechnikov Kernel', fontsize=14)
    ax[1].set_ylabel('Density', fontsize=14)
    ax[1].set_xlabel('Age', fontsize=14)

    ax[2].plot(data, np.exp(kd_exponential.score_samples(data)))
    ax[2].set_title('Exponential Kernel', fontsize=14)
    ax[2].set_xlabel('Age', fontsize=14)
    ax[2].set_ylabel('Density', fontsize=14)

    plt.show()

    # Perform a sample anomaly detection
    test_data = np.array([12, 15, 18, 20, 25, 30, 40, 50, 55, 60, 65, 70, 75, 80, 85, 90]).reshape(-1, 1)

    test_densities_epanechnikov = np.exp(kd_epanechnikov.score_samples(test_data))
    test_densities_gaussian = np.exp(kd_gaussian.score_samples(test_data))

    for age, density in zip(np.squeeze(test_data), test_densities_epanechnikov):
        print('p(Age = {:d}) = {:.7f} ({})'.format(age, density, 'Anomaly' if density < 0.005 else 'Normal'))


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Fourier transform using Python (version 3.5).

This code accompanies the book: The Fourier Transform: A Tutorial Introduction by James V Stone.
https://jim-stone.staff.shef.ac.uk/Fourier

To run code, set testing to 0 to analyse data in supplied sound file or 1 to test code on synthetic data containing just two frequencies.

Note that this code is written for transparency, not speed. It does not use complex numbers. Instead the Fourier coefficients are found using sine and cosine functions. The corresponding MatLab code generated Figures 1.8a and 1.8b in the book (this Python code provides similar figures).

Spyder version: 4.1.5 None
Python version: 3.8.1 64-bit
Tested on mac OS 10.15.7 (Catalina)
@author: JimStone

Copyright: All except the sound files are public domain.
The sound files are sebinpubaudiIMG_1563.wav
# and sebinpubaudiIMG_1563.m4a.

#############################
# Simple Fourier transform key variables
#
# x       signal of N sampled values
# T       length of signal in seconds
# R       sampling rate in samples per second
# N       number of samples in signal
# nvec    vector of N ample numbers
# tvec    vector of N time values in seconds
# fundamental  vector of N elements
# containing values from 0 to 2*pi =
# fundamental frequency
# fmax  maximum frequency in Hz, chosen by user
# dt        interval in seconds between samples  = 1/R
# fmin    lowest frequency in Hz = 1/T
# nfrequencies  number of frequencies in transform
# Cs    vector of nfrequencies cosine coefficients
# Ds    vector of nfrequencies sine coefficients
# As    vector of nfrequencies Fourier amplitudes
# freqs vector of frequency values in Hz
#############################
"""

import numpy as np
import matplotlib.pyplot as plt
from scipy.io import wavfile

testing = 0
# set to 1 for synthetic data, and 0 for voice data.
fs = 10 # set fontsize for graphs.

###############################################
if testing: # make simple data to test program.
    frequency = 500;
    # insert this frequency Hz
    T = 0.5; # length of signal in seconds

    period = 1/frequency; # in seconds
    R = 44100; # set sampling rate, samples/s
    N = R*T; # number of samples in T seconds
    tvec = np.arange(0, N, 1)/R;
    # time vector in units of seconds

    # make vector containg 1 frequency = 1/period.
    x1 = 2 * np.pi * tvec/period;

    # signal in which each interval of period
    # seconds increases by 1.5*2*pi.
    x2 = 1.5*x1; # make sinusoids
    x = np.sin(x1);
    x = x + 0.5*np.sin(x2);
else: # use data from sound file
    # set name of sound file
    fname = 'sebinpubaudiIMG_1563.wav';
    # read in data from file.
    R, x = wavfile.read(fname)
    # set length of sound segment in seconds.
    segmentseconds = 0.5;
    N = int(R * segmentseconds);
    # sample number at end of segmentseconds.
    # set start and end indices in data samples.
    xmin = 0;
    xmax = xmin+N;
    x = x[xmin:xmax];
    T = N/R; # number of seconds in recording
# endif
###############################################

N = len(x); # number of samples.

# specify maximum frequency to be analysed here
fmax = 1000; # Hz

nvec = np.arange(0, N, 1);
# vector of N samples
tvec = np.arange(0, N, 1)/R;
# vector of N samples in units of seconds

# Plot first part of signal.
nforplot=500; # number of samples for graph.
plt.figure("Signal")
plt.clf()
plt.title("Signal",fontsize=fs)
plt.xlabel("Time (seconds)",fontsize=fs)
plt.ylabel("Amplitude",fontsize=fs)
plt.plot(tvec[0:nforplot],x[0:nforplot])
plt.show()

# make fundamental sinusoidal vector.
nvec = np.arange(0,N); # indices = 0->N-1
fundamental = nvec * 2 * np.pi / N;
# fundamental now spans up to 2pi.

plt.figure("Fundamental")
plt.clf()
plt.title("Fundamental + one harmonic",fontsize=fs)
plt.xlabel("Time (seconds)",fontsize=fs)
plt.ylabel("Amplitude",fontsize=fs)
plt.plot(tvec,np.sin(fundamental),label="fundamental")
plt.plot(tvec,np.sin(2*fundamental),label="first harmonic")
plt.legend()
plt.show()

dt = 1/R; # interval between samples.
T = N/R;
fmin = 1.0/T; # lowest frequency in Hz.
# number of frequencies
nfrequencies = np.ceil(fmax/fmin);
nfrequencies = int(nfrequencies)

# make arrays to store Fourier coefficients.
Cs = np.arange(nfrequencies) * 0;
Ds = np.arange(nfrequencies) * 0;
# make array to store frequency values.
freqs = np.arange(nfrequencies) * 0;

###############################################
# Here is where the work gets done.
for n in range(1, nfrequencies):

    freq = fmin*n;
    freqs[n] = freq;

    # make cosine wave at frequency freq.
    cosinewave = np.cos(fundamental*n);
    # find inner product of sound with cosine wave.
    C = x.dot(cosinewave);
    Cs[n-1] = C;

    # make sine wave at frequency freq.
    sinewave = np.sin(fundamental*n);
    # find inner product of sound with sine wave.
    D = x.dot(sinewave);
    Ds[n-1] = D;
# work now done.
###############################################

# find amplitude of components, normalise by N.
As = ((Cs**2 + Ds**2) ** 0.5) / N

# Plot amplitude spectrum
plt.figure("Amplitude spectrum")
plt.clf()
plt.title("Amplitude spectrum",fontsize=fs)
plt.plot(freqs,As)
plt.xlabel("Frequency (Hz)",fontsize=fs)
plt.ylabel("Amplitude",fontsize=fs)

print('\nNumber of frequencies = ',nfrequencies)
print('\nFourier program completed.')
###############
# END OF FILE
################


# Lab1 -> 12.10

import numpy as np
import matplotlib.pyplot as plt
import metode_numerice_ecuatii_algebrice as mnea
import sympy as sym

"""
Lab#2.Ex2.b: Să se identifice intervalele pe care funcția f admite o sol unică.
             f(x) = x^3 - 7*(x^2) + 14x - 6
             Interval (a, b) = (0, 4)
"""

def f(x):
    y = x**3 - 7*x**2 + 14*x - 6
    return y

(a, b) = (0, 4)
interval = mnea.cauta_intervale(f, a, b, 10)
print(interval)
dim = np.shape(interval)[1]  #shape: returnează dimensiunile matricei

#Desen Grafice
x_grafic = np.linspace(0, 4, 100)
y_grafic = f(x_grafic)
plt.plot(x_grafic, y_grafic, marker = 'o', color = 'blue', linewidth = 3)
plt.axvline(0, color = 'black')
plt.axhline(0, color = 'black')
plt.xlabel('X')
plt.ylabel('Y')
plt.title('Lab#2.Ex2.b)')
plt.grid(True)
for i in range(dim):
    plt.plot(interval[:, i], np.zeros((2, 1)), color = 'red', linewidth = 10)
plt.show()

"""
Lab#2.Ex2.a: Pt datele de mai sus, să se afle toate rădăcinile conform
             metodei bisecției.
"""

r = np.zeros(dim)
for i in range(dim):
    r[i], N = mnea.bisectie(f, interval[0,i], interval[1, i], 10**-3)
    print("Metoda Bisectiei")
    print("Ecuația x^3 - 7*(x^2) + 14x - 6 = 0")
    print("Intervalul [{:.3f}, {:.3f}]".format(interval[0,i], interval[1,i]))
    print("Solutia Numerica: x *= {:.3f}".format(r[i]))
    print("-----------------------------------")

plt.plot(r, f(r), 'o-', color = 'green', markersize = 10)
plt.show()

# ---------------------------------------------
# Lab2 -> 19.10

"""
Lab#2.Ex3:
"""

x = sym.symbols('X')           # var simbolica
f_expr = f(x)                  # expresia simbolica
df_expr = sym.diff(f_expr, x)
df = sym.lambdify(x, df_expr)  # functie acum
print("--------------------------------------------------------------------------------------")
print(f'Expresia: {f_expr}')
print(f'Derivata calculata: {df_expr}')
print(f'Derivata de 2: {df(2)}')

#Construim functia pe [0, 4]
(a, b) = (0, 4)
n_noduri = 100
x_grafic = np.linspace(a, b, n_noduri)
y_grafic = f(x_grafic)
plt.plot(x_grafic, y_grafic, linewidth = 3)
plt.grid()
plt.axvline(0, color = 'black') #ax vertical line
plt.axhline(0, color = 'black')

# Definim x0 = vector cu valoarea de pornire pt fiecare radacina
# Aleg x0 a.î. f(x0) * f''(x0) > 0
# f(x0) < 0 --> la stanga (pt prima radacina)
x0 = np.array([0.3, 2.5, 4])
sols = np.zeros(x0.shape)
iteratii = np.zeros(sols.shape)
eps = 10 ** (-6)
for i in range(len(x0)):
    sols[i], iteratii[i] = mnea.NewtonRap(f, df, x0[i], eps)

plt.plot(sols, f(sols), 'o', markerfacecolor = 'red', markersize = 10)
plt.show()

"""
Lab#2.Ex4:
"""

x_grafic = np.linspace(-1, 1.5, 100)
y_grafic = f(x_grafic)
plt.plot(x_grafic, y_grafic, linewidth = 3)
plt.grid()
plt.axvline(0, color = 'black') #ax vertical line
plt.axhline(0, color = 'black')
plt.show()
x0 = -1
eps = np.linspace(10 ** -13, 10 ** -3, 50)
N_bis2 = np.zeros(eps.shape)
N_NR = np.zeros(eps.shape)
for i in range(len(eps)):
    sol_bis, N_bis2[i] = mnea.bisectie2(f, -1, 1.5, eps[i])
    sol_NR, N_NR[i] = mnea.NewtonRap(f, df, x0, eps[i])

plt.plot(eps, N_bis2, 'b', linewidth = 2)
plt.plot(eps, N_NR, 'r', linewidth = 2)
plt.grid()
plt.axvline(0, color = 'black')
plt.axhline(0, color = 'black')
plt.show()

Calcul Numeric (CN)/Laborator/Laborator 1, 2/rezolvareaNumericaAEcAlg.py


"""
solve the diffusion equation:

 phi_t = k phi_{xx}

with a first-order (in time) implicit discretization

M. Zingale (2013-04-03)
"""

import numpy as np
from scipy import linalg
import matplotlib.pyplot as plt
from diffusion_explicit import Grid1d

class Simulation(object):

    def __init__(self, grid, k=1.0):
        self.grid = grid
        self.t = 0.0
        self.k = k  # diffusion coefficient

    def init_cond(self, name, *args):
        # initialize the data

        if name == "gaussian":
            t0, phi1, phi2 = args
            self.grid.phi[:] = self.grid.phi_a(0.0, self.k, t0, phi1, phi2)

    def diffuse_implicit(self, dt):
        """ diffuse phi implicitly through timestep dt """

        gr = self.grid
        phi = gr.phi

        phinew = gr.scratch_array()

        alpha = self.k*dt/gr.dx**2

        # create the RHS of the matrix
        R = phi[gr.ilo:gr.ihi+1]

        # create the diagonal, d+1 and d-1 parts of the matrix
        d = (1.0 + 2.0*alpha)*np.ones(gr.nx)
        u = -alpha*np.ones(gr.nx)
        u[0] = 0.0

        l = -alpha*np.ones(gr.nx)
        l[gr.nx-1] = 0.0

        # set the boundary conditions by changing the matrix elements

        # homogeneous neumann
        d[0] = 1.0 + alpha
        d[gr.nx-1] = 1.0 + alpha

        # solve
        A = np.matrix([u,d,l])
        phinew[gr.ilo:gr.ihi+1] = linalg.solve_banded((1,1), A, R)

        return phinew

    def evolve(self, C, tmax):
        """
        the main evolution loop.  Evolve

        phi_t = k phi_{xx}

        from t = 0 to tmax
        """

        gr = self.grid

        # time info
        dt = C*0.5*gr.dx**2/self.k

        while self.t < tmax:

            # make sure we end right at tmax
            if self.t + dt > tmax:
                dt = tmax - self.t

            # diffuse for dt
            phinew = self.diffuse_implicit(dt)

            gr.phi[:] = phinew[:]
            self.t += dt

if __name__ == "__main__":

    # reference time
    t0 = 1.e-4

    # state coeffs
    phi1 = 1.0
    phi2 = 2.0

    k = 1.0

    #-------------------------------------------------------------------------
    # normal time

    tmax = 0.005

    nx = 128

    Cs = [0.8, 2.0, 10.0]
    colors = ["b", "g", "r"]

    for C in Cs:
        gr = Grid1d(nx, ng=1)
        s = Simulation(gr, k=k)
        s.init_cond("gaussian", t0, phi1, phi2)
        s.evolve(C, tmax)

        plt.plot(gr.x[gr.ilo:gr.ihi+1], gr.phi[gr.ilo:gr.ihi+1],
                 color=colors.pop(), label="C = {}".format(C))

    # analytic solution
    plt.plot(gr.x[gr.ilo:gr.ihi+1],
               gr.phi_a(tmax, k, t0, phi1, phi2)[gr.ilo:gr.ihi+1],
               ls=":", color="0.5", label="analytic solution")

    plt.legend(frameon=False)

    plt.xlabel("$x$", fontsize="large")
    plt.ylabel(r"$\phi$", fontsize="large")
    plt.title("Backward-difference implicit diffusion, nx = {}, C = {:3.2f}, t = {:5.2g}".format(nx, C, tmax))

    plt.savefig("diffimplicit.pdf")

    #-------------------------------------------------------------------------
    # early time

    plt.clf()

    tmax = 0.0005

    nx = 128

    colors = ["b", "g", "r"]

    for C in Cs:
        gr = Grid1d(nx, ng=1)
        s = Simulation(gr, k=k)
        s.init_cond("gaussian", t0, phi1, phi2)
        s.evolve(C, tmax)

        plt.plot(gr.x[gr.ilo:gr.ihi+1], gr.phi[gr.ilo:gr.ihi+1],
                 color=colors.pop(), label="C = {}".format(C))

    # analytic solution
    plt.plot(gr.x[gr.ilo:gr.ihi+1],
               gr.phi_a(tmax, k, t0, phi1, phi2)[gr.ilo:gr.ihi+1],
               ls=":", color="0.5", label="analytic solution")

    plt.legend(frameon=False)

    plt.xlim(0.3,0.7)

    plt.xlabel("$x$", fontsize="large")
    plt.ylabel(r"$\phi$", fontsize="large")
    plt.title("Backward-difference implicit diffusion, nx = {}, C = {:3.2f}, t = {:5.2g}".format(nx, C, tmax))

    plt.savefig("diffimplicit-early.pdf")


# solutions.py
"""Volume 2 Lab 19: Interior Point 1 (Linear Programs). Solution file."""

import numpy as np
from scipy import linalg as la
from scipy.stats import linregress
from matplotlib import pyplot as plt

# Auxiliary Functions =========================================================
def startingPoint(A, b, c):
    """Calculate an initial guess to the solution of the linear program
    min c^T x, Ax = b, x>=0.
    Reference: Nocedal and Wright, p. 410.
    """
    # Calculate x, lam, mu of minimal norm satisfying both
    # the primal and dual constraints.
    B = la.inv(A.dot(A.T))
    x = A.T.dot(B.dot(b))
    lam = B.dot(A.dot(c))
    mu = c - A.T.dot(lam)

    # Perturb x and s so they are nonnegative.
    dx = max((-3./2)*x.min(), 0)
    dmu = max((-3./2)*mu.min(), 0)
    x += dx*np.ones_like(x)
    mu += dmu*np.ones_like(mu)

    # Perturb x and mu so they are not too small and not too dissimilar.
    dx = .5*(x*mu).sum()/mu.sum()
    dmu = .5*(x*mu).sum()/x.sum()
    x += dx*np.ones_like(x)
    mu += dmu*np.ones_like(mu)

    return x, lam, mu

def randomLP(m,n):
    """Generate a linear program min c^T x s.t. Ax = b, x>=0.
    First generate m feasible constraints, then add
    slack variables to convert it into the above form.
    Inputs:
        m -- positive integer >= n, number of desired constraints
        n -- dimension of space in which to optimize
    Outputs:
        A -- array of shape (m,n+m)
        b -- array of shape (m,)
        c -- array of shape (n+m,), with m trailing 0s
        v -- the solution to the LP
    """
    # generate random constraints (each row corresponds to the normal
    # vector defining a linear constraint)
    A = np.random.random((m,n))*20 - 10

    # adjust so that the normal vector of each constraint lies in the upper
    # half-space. this ensures that the constraints permit a feasible region
    A[A[:,-1]<0] *= -1

    # adjust so that the solution to the program is a prescribed point v in
    # the first quadrant.
    v = np.random.random(n)*10
    #k = np.random.randint(n,m+1)
    k = n
    b = np.zeros(m)
    b[:k] = A[:k,:].dot(v)
    b[k:] = A[k:,:].dot(v) + np.random.random(m-k)*10

    # now create the appropriate c vector, a weighted sum of the first k
    # constraints
    c = np.zeros(n+m)
    c[:n] = A[:k,:].sum(axis=0)/k

    # at this point, we should have a program max c^T x s.t. Ax <= b, x >= 0
    # we need to convert it to standard equality form by adding slack variables
    A = np.hstack((A, np.eye(m)))

    # we now have the program min -c^T x s.t. Ax = b, x>=0.
    # the optimal solution has x[:n] = v
    return A, b, -c, v

# Problems 1-4 ================================================================
def interiorPoint(A, b, c, niter=20, tol=1e-16, verbose=False):
    """Solve the linear program min c^T x, Ax = b, x>=0
    using an Interior Point method.

    Parameters:
        A ((m,n) ndarray): Equality constraint matrix with full row rank.
        b ((m, ) ndarray): Equality constraint vector.
        c ((n, ) ndarray): Linear objective function coefficients.
        niter (int > 0): The maximum number of iterations to execute.
        tol (float > 0): The convergence tolerance.

    Returns:
        x ((n, ) ndarray): The optimal point.
        val (float): The minimum value of the objective function.
    """
    m,n = A.shape
    def F(x_, l_, m_):
        """The almost-linear function that accounts for the KKT conditions."""
        return np.hstack((np.dot(A.T, l_)+m_-c, np.dot(A, x_)-b, m_*x_))

    DF = np.zeros((2*n+m, 2*n+m))
    DF[:n,n:-n] = A.T
    DF[:n,-n:] = np.eye(n)
    DF[n:-n,:n] = A

    # Get the initial point and verify the dimensions.
    x, lam, mu = startingPoint(A, b, c)
    assert len(x) == len(mu) == len(c) == n
    assert len(lam) == len(b) == m

    e = np.ones_like(mu)
    sigma = .1

    i = 0
    nu = 1 + tol
    while i < niter and nu >= tol:
        i += 1

        # Problem 3: Search Direction
        DF[-n:,:n] = np.diag(mu)
        DF[-n:,-n:] = np.diag(x)

        nu = np.dot(x, mu) / n
        nu_vec = np.hstack((np.zeros(n+m), e*nu*sigma))
        lu_piv = la.lu_factor(DF)
        direct = la.lu_solve(lu_piv, nu_vec - F(x,lam,mu))

        # Problem 4: Step Length
        dx, dlam, dmu = direct[:n], direct[n:-n], direct[-n:]

        mask = dmu < 0
        amin = np.min(-mu[mask]/dmu[mask])
        alpha = min(1, .95*min(1, amin)) if np.any(mask) else .95

        mask = dx < 0
        dmin = np.min(-x[mask]/dx[mask]).min()
        delta = min(1, .95*min(1, dmin)) if np.any(mask) else .95

        # Problem 5: Finish it up.
        x += delta*dx
        lam += alpha*dlam
        mu += alpha*dmu

        if verbose:
            print("Iteration {:0>2} nu = {}".format(i, nu))
    if i < niter and verbose:
        print("Converged in {} iterations".format(i))
    elif verbose:
        print("Maximum iterations reached")
    return x, c.dot(x)

# Tests =======================================================================
def single_test(m=7, n=5, verbose=True):
    """Test interiorPoint() a single time."""
    A, b, c, x = randomLP(m, n)
    point, value = interiorPoint(A, b, c, verbose=verbose)
    if not np.allclose(x,point[:n]):
        return 1
    return 0

def single_other_test():
    m = np.random.randint(2,10)
    n = m
    A = np.random.random((m,n))*20 - 10
    A[A[:,-1]<0] *= -1
    x = np.random.random(n)*10
    b = A.dot(x)
    c = -A.sum(axis=0)/float(n)

    point, value = interiorPoint(A, b, c, verbose=True)
    if not np.allclose(x,point):
        return 1
    return 0

def huge_test(trials=100):
    """Test interior point in high dimensions 'trials' times."""
    from sys import stdout
    failed = 0
    for i in xrange(trials):
        print "\rIteration {} of {}".format(i+1, trials),
        stdout.flush()
        n = np.random.randint(low=100, high=200)
        m = np.random.randint(low=n, high=300)
        failed += test_interiorPoint(m, n, verbose=False)
        print "({} failures)".format(failed),
    print "{}/{} failed".format(failed, trials)

# Problem 5 ===================================================================
def leastAbsoluteDeviations(save=False):
    """This code should be fairly close to what the students submit for the
    least absolute deviations problem.
    """
    data = np.loadtxt('simdata.txt')
    m = data.shape[0]
    n = data.shape[1] - 1
    c = np.zeros(3*m + 2*(n + 1))
    c[:m] = 1
    y = np.empty(2*m)
    y[::2] = -data[:, 0]
    y[1::2] = data[:, 0]
    x = data[:, 1:]
    A = np.ones((2*m, 3*m + 2*(n + 1)))
    A[::2, :m] = np.eye(m)
    A[1::2, :m] = np.eye(m)
    A[::2, m:m+n] = -x
    A[1::2, m:m+n] = x
    A[::2, m+n:m+2*n] = x
    A[1::2, m+n:m+2*n] = -x
    A[::2, m+2*n] = -1
    A[1::2, m+2*n+1] = -1
    A[:, m+2*n+2:] = -np.eye(2*m, 2*m)

    sol = interiorPoint(A, y, c, niter=10, verbose=True)[0]
    beta = (sol[m:m+n] - sol[m+n:m+2*n])[0]
    b = sol[m+2*n] - sol[m+2*n+1]

    dom = np.linspace(0,10,2)
    plt.scatter(data[:,1], data[:,0], c='k')
    plt.plot(dom, beta*dom+b, 'b-', linewidth=2, label="LAD")

    # Compare with least squares
    slope, intercept = linregress(data[:,1], data[:,0])[:2]
    lstsq_line = slope*dom + intercept
    plt.plot(dom, lstsq_line, 'g--', linewidth=2, label="LSTSQ")

    # plt.title("Problem 5")
    plt.legend()
    if save is True: # For figure file creation
        plt.savefig("LADprob.pdf")
    else:
        plt.show()
    print 'Beta:', beta
    print 'b:', b

# =============================================================================
# Solutions using the old method (Predictor Corrector) ========================
# =============================================================================
def old_interiorPoint(A, b, c, niter=20, verbose=False, starting_point=None, pts=False):
    '''
    Solve the linear programming problem min c^T x, Ax = b, x>=0
    using an Interior Point method. This code is not optimized, but
    forms the basis for a common practical approach known as the
    Predictor-Corrector Algorithm.
    Inputs:
        A -- array of shape (m,n) with linearly independent rows
        b -- array of length m
        c -- array of length n
        niter -- positive integer giving the number of iterations
        starting_point -- tuple of arrays giving the initial values for x, l, and s.
                          if unspecified, the function startingPoint is used.
    Returns:
        x -- the optimal point
        val -- the minimum value of the objective function
        (pts -- list of points traced by the algorithm, returned if pts=True)
    Ref: Nocedal and Wright, p. 411
    '''
    pts = []
    # initialize variables
    m,n = A.shape
    if starting_point:
        x, l, s = starting_point
    else:
        x,l,s = startingPoint(A,b,c)
    pts.append(x)
    N = np.zeros((n+m+n, n+m+n))
    N[:n, n:n+m] = A.T
    N[:n, n+m:] = np.eye(n)
    N[n:n+m, :n] = A
    sol = np.empty(n+m+n)
    for k in xrange(niter):
        # finish initializing parts of the step equation
        N[n+m:, :n] = np.diag(s)
        N[n+m:, n+m:] = np.diag(x)
        r_c = (A.T).dot(l)+s-c
        r_b = A.dot(x)-b
        rhs = np.hstack((-r_c.ravel(), -r_b.ravel(), -x*s))

        # solve dx_aff, dl_aff, ds_aff using LU decomposition
        lu_piv = la.lu_factor(N)
        sol[:] = la.lu_solve(lu_piv, rhs)
        dx_aff = sol[:n]
        dl_aff = sol[n:n+m]
        ds_aff = sol[n+m:]

        # calculate a_p, a_d, mu_aff
        mask1 = dx_aff < 0
        if mask1.sum() > 0:
            a_p = min(1, ((-x/dx_aff)[mask1]).min())
        else:
            a_p = 1
        mask2 = ds_aff < 0
        if mask2.sum() > 0:
            a_d = min(1, (-s/ds_aff)[mask2].min())
        else:
            a_d = 1
        mu_aff = ((x+a_p*dx_aff)*(s+a_d*ds_aff)).sum()/np.float(n)

        # calculate mu times the centering parameter sig
        mu = (x*s).sum()/n
        musig = mu_aff**3/mu**2

        # calculate dx, dl, ds
        rhs[n+m:] += - dx_aff*ds_aff + musig
        sol[:] = la.lu_solve(lu_piv, rhs)
        dx = sol[:n]
        dl = sol[n:n+m]
        ds = sol[n+m:]

        # calculate ap, ad
        nu = 1-.1/(k+1)
        mask3 = dx < 0
        if mask3.sum() > 0:
            ap_max = (-x/dx)[mask3].min()
            ap = min(1, nu*ap_max)
        else:
            ap = 1
        mask4 = ds < 0
        if mask4.sum() > 0:
            ad_max = (-s/ds)[mask4].min()
            ad = min(1, nu*ad_max)
        else:
            ad = 1

        # step to new point
        x = x + ap*dx
        l = l + ad*dl
        s = s + ad*ds
        pts.append(x)

        if verbose:
            print '{0:f} {1:f}'.format((c*x).sum(), mu)

    if pts:
        return pts
    else:
        return x, (c*x).sum()


# Name: Breno Maurício de Freitas Viana
# NUSP: 11920060
# Course Code: SCC5830
# Year/Semester: 2021/1
# Assignment 3: Filtering

import math
import numpy as np
import imageio

# ----- (1) Read Parameters

# Get the location of the reference image `I`
imgref = input().rstrip()
# Get the filtering method identifier `F`
F = int(input())

# --- Read the filter parameters
size = 0
filter = None

if F == 1:
  size = int(input().rstrip())
  filter = np.fromstring(input().rstrip(), dtype=np.int32, sep=' ')
if F == 2:
  size = int(input().rstrip())
  filter = np.empty((size, size), dtype=np.int32)
  for i in range(size):
    filter[i] = np.fromstring(input().rstrip(), dtype=np.int32, sep=' ')
if F == 3:
  size = int(input().rstrip())

# --- Load image
I = imageio.imread(imgref)

# ----- (2) - Filtering

# --- Convolution functions

def conv_point1d(f, w, x, a):
  """
  Return the convolution of a 1D point.
  """
  # Get the subarray
  sub_f = f[x - a:x + a + 1]
  # Apply filter in the point x
  return np.sum(np.multiply(sub_f, w))

def conv_point2d(f, w, x, y, a, b):
  """
  Return the convolution of a 2D point.
  """
  # Create the sub 2d array
  sub_f = f[x - a:x + a + 1, y - b:y + b + 1]
  # Apply filter in the point x, y
  return np.sum(np.multiply(sub_f, w))

# Median filter

def median(f, x, y, a, b):
  """
  Return the median value of the `size`-neighbors of the given point.
  """
  # Create the sub 2d array
  sub_f = f[x - a:x + a + 1, y - b:y + b + 1]
  # Return the median
  arr = np.sort(np.asarray(sub_f).reshape(-1))
  return np.median(arr)

# --- Filters

def f1():
  """
  Filtering 1D.
  """
  # Get center of the filter
  c = int((size - 1) / 2)
  # Pad the flatten (1D array) image with wrapping
  If = np.pad(I.flatten(), (c), 'wrap')
  # Initialize the resulting image
  Ir = np.zeros(If.shape)
  # Apply 1D convulation in the image
  for x in range(c, Ir.shape[0] - c):
    Ir[x] = conv_point1d(If, filter, x, c)
  # Remove padding
  Ir = Ir[c:-c]
  # Return the resulting image with original shape
  return Ir.reshape(I.shape)

def f2():
  """
  Filtering 2D.
  """
  # Get center of the filter
  c = int((size - 1) / 2)
  # Pad the original image with edge
  Ip = np.pad(I, (c, c), 'edge')
  # Initialize the resulting image
  Ir = np.zeros(Ip.shape)
  N, M = Ir.shape
  for x in range(c, N - c):
    for y in range(c, M - c):
      Ir[x, y] = conv_point2d(Ip, filter, x, y, c, c)
  # Remove padding
  Ir = Ir[c:-c, c:-c]
  # Return the resulting image
  return Ir.reshape(I.shape)

def f3():
  """
  Median Filter.
  """
  a = int((size - 1) / 2)
  b = int((size - 1) / 2)
  # Pad the original image with constant
  Ip = np.pad(I, (size, size), 'constant', constant_values=(0, 0))
  # Initialize the resulting image
  Ir = np.zeros(Ip.shape)
  N, M = Ir.shape
  for x in range(size, N - size):
    for y in range(size, M - size):
      Ir[x, y] = median(Ip, x, y, a, b)
  # Remove padding
  Ir = Ir[size:-size, size:-size]
  # Return the resulting image
  return Ir.reshape(I.shape)

# --- Apply the given filter
def apply_filter():
  if F == 1:
    return f1()
  if F == 2:
    return f2()
  if F == 3:
    return f3()

# Apply filter
Ir = apply_filter()

# Normalize resultinrectg image and convert the values to uint8
Ir = (Ir - Ir.min()) / (Ir.max() - Ir.min())
Ir *= 255
Ir = Ir.astype(np.uint8)

# ----- (3) Comparing `Ir` against reference image `I`

# --- Calculate the root mean squared error
def rmse(I, Ir):
  N, M = I.shape
  rmse = 0.0
  for x in range(N):
    for y in range(M):
      rmse += math.pow(float(I[x, y]) - float(Ir[x, y]), 2)
  rmse = math.sqrt(rmse / (N * M))
  return round(rmse, 4)

# --- Print the computed result of RMSE
print(rmse(I, Ir))


import numpy as np
import matplotlib
matplotlib.rcParams = matplotlib.rc_params_from_file('../../matplotlibrc')
from matplotlib import pyplot as plt

def lnseries():
    ax = plt.subplot(1, 1, 1)
    X = np.linspace(-5, 30, 17501)
    Y = np.log(10**(-X) + 1) / 10**(-X)
    plt.plot(X, Y, label="$10^x \\log(10^{-x}+1)$")
    X = np.linspace(1, 30, 291)
    poly = np.poly1d([(-1.)**(i+1)/i for i in xrange(1,30)][::-1])
    Y = poly(10**(-X))
    plt.plot(X, Y, label="Series Approximation")
    handles, labels = ax.get_legend_handles_labels()
    plt.legend(handles, labels, loc="upper left")
    plt.savefig("lnseries.pdf")
    plt.clf()

def pnorm(x, p):
    P = p.reshape((1, -1))
    X = x.reshape((-1, 1))
    return ((X**P).sum(axis=0)**(1./P)).ravel()

def pnorm_convergence():
    ax = plt.subplot(1, 1, 1)
    powers = np.linspace(1, 7, 701)
    X = np.random.rand(100)+1
    N = pnorm(X, 10**powers)
    plt.plot([1,7], [X.max()]*2, label="$\\|x\\|_{\\infty}$")
    plt.plot(powers, N, label="p-norms of $x$")
    handles, labels = ax.get_legend_handles_labels()
    plt.legend(handles, labels, loc="upper right")
    plt.xlabel("$\\log_{10} p$")
    plt.ylabel("$\\| x \\|_p$")
    plt.savefig("pnorm_convergence.pdf")
    plt.clf()

if __name__ == "__main__":
    lnseries()
    pnorm_convergence()


import matplotlib
matplotlib.rcParams = matplotlib.rc_params_from_file('../../matplotlibrc')

import numpy as np
from matplotlib import pyplot as plt

K = plt.imread('cameraman.tif')
blur = np.array([[2,4,5,4,2],
                 [4,9,12,9,4],
                 [5,12,15,12,5],
                 [4,9,12,9,4],
                 [2,4,5,4,2]])/159.

def Filter(I, f):
    '''
    Filter an image I with matrix filter f.
    Inputs:
        f -- numpy array of shape (m,n), where m and n are odd
        I -- numpy array of shape (k,l), the image
    Outpus:
        O -- numpy array of shape (k,l), the filtered image
    '''
    m,n = f.shape
    k,l = I.shape

    #initialize the output array
    O = np.empty((k,l))

    #create a padded version of I by padding with zeros
    N = np.zeros((k+m-1, l+n-1))
    N[m/2:m/2+k, n/2:n/2+l] = I

    #set the ouput pixels
    for i in xrange(k):
        for j in xrange(l):
            O[i,j] = (f*N[i:i+m,j:j+n]).sum()
    return O

def plotEdges(I):
    '''
    Find and plot the edges of an image I using the Sobel filter.
    Inputs:
        I -- numpy array of shape (m,n), the image
    Returns:
        This function returns nothing, but the last line is a call
        to the function plt.show() from matplotlib.pyplot
    '''
    #create the Sobel filter (for the vertical gradient)
    S = np.array([[-1,-2,-1],
                  [0,0,0],
                  [1,2,1]])/8.

    #filter the image horizontally and vertically to get gradient values
    Oy = Filter(K, Sobel)
    Ox = Filter(K, Sobel.T)

    #combine to obtain gradient magnitude at each pixel
    O = np.sqrt(Oy**2+Ox**2)

    #set threshold value
    thresh = 4*O.mean()

    #plot the thresholded image
    plt.imshow(O > thresh, cmap=plt.cm.Greys_r)
    plt.show()

def cameramanClean():
    plt.imsave('cameramanClean.pdf',np.flipud(K), origin='lower')
    plt.clf()

def cameramanBlur():
    O = Filter(K, blur)
    plt.imsave('cameramanBlur.pdf', np.flipud(O), origin='lower')
    plt.clf()

def edges():
    S = np.array([[-1,-2,-1],
                  [0,0,0],
                  [1,2,1]])/8.

    #filter the image horizontally and vertically to get gradient values
    Oy = Filter(K, S)
    Ox = Filter(K, S.T)

    #combine to obtain gradient magnitude at each pixel
    O = np.sqrt(Oy**2+Ox**2)

    #set threshold value
    thresh = 4*O.mean()

    #plot the thresholded image
    plt.imsave('edges.pdf',np.flipud(O>thresh), origin='lower')
    plt.clf()

cameramanClean()
cameramanBlur()
edges()


#!/usr/bin/python
#
# Przyklad jednoczesnego sortowania wartosci wlasnych i wektorow wlasnych.
# The eigenvalues are not necessarily ordered.
# help(numpy.argsort)
# argsort(a, axis=-1, kind='quicksort', order=None)
# Returns the indices that would sort an array.
#
# https://stackoverflow.com/questions/33037616/large-matrix-diagonalization-python

import math
import numpy

A = numpy.array([[3,1,-1], [1,3,-1], [-1,-1,5]], dtype=float)
print ( A )
# Wartosci wlasne to 2, 3, 6, ale kolejnosc moze byc rozna.
# Wektory wlasne beda ulozone kolumnami!
# The column vec[:,i] is the eigenvector corresponding to the eigenvalue w[i].
w, vec =  numpy.linalg.eig(A)
#print (w, vec)
print ("eigenvalues", w)
print ("unit eigenvectors (in columns)")
print (vec)

# Zmieniam kolejnosc wartosci wlasnych i wektorow wlasnych.
print ( "Zmiana kolejnosci ..." )
idx = w.argsort()[::-1] #large to small
# idx = w.argsort() #small to large
# idx to jest index_array : ndarray, int.
w = w[idx]   # to dziala elementwise, faktycznie sortuje w
vec = vec[:,idx]   # wiersze po kolei, kolumny wg idx
print (w, vec)
# now they are ordered and you can iterate through your results
# to write them to your file

print ( "Sprawdzenie dzialania macierzy na wektory ..." )
# Mnozenie macierzy i wektora wlasnego ma byc rowne mnozeniu
# wartosci wlasnej i wektora wlasnego.
v0 = vec[:,0] # kolejne wektory wlasne z kolumn
v1 = vec[:,1]
v2 = vec[:,2]
print ( "A v", numpy.dot(A,v0) )
print ( "lam v", w[0] * v0 )
print ( "A v", numpy.dot(A,v1) )
print ( "lam v", w[1] * v1 )
print ( "A v", numpy.dot(A,v2) )
print ( "lam v", w[2] * v2 )   # bledy maszynowe

print ( "Diagonalizacja B^T A B ..." )
print ( numpy.dot(numpy.transpose(vec), numpy.dot(A,vec)) ) # dziala!

# EOF


import numpy as np

# Task 1
def array_filter(array: np.array, t: int) -> list:
    return [(x, y) for x, y in np.ndindex(array.shape) if array[x, y] >= t]

# Task 2
def get_queen_moves(x: int, y: int) -> np.array:
    return np.array([[1 if ((i == x and j != y)
                            or (j == y and i != x)
                            or abs(x - i) == abs(y - j) > 0)
                      else 0 for i in range(8)] for j in range(8)])

# Task 3
def add_reduce_board(board: list) -> np.array:
    return np.transpose(np.add.reduce(np.array(board)))

def check_queen_puzzle(chessboard: np.array) -> bool:
    positions = array_filter(chessboard, 1)
    board = add_reduce_board([get_queen_moves(p[0], p[1]) for p in positions])
    return not any([board[pos] for pos in positions])

# Task 4
def generate_system(solution: np.array) -> tuple:
    n = solution.shape[0]
    coefficients = np.random.uniform(0, 100, (n, n))
    np.fill_diagonal(coefficients, 0)
    non_diag = np.sum(abs(coefficients))
    np.fill_diagonal(coefficients, non_diag + np.random.uniform(0, non_diag))
    return coefficients, coefficients @ solution

# Task 5
def solve_system(coefficients: np.array, free_terms: np.array,
                 threshold=0.01, limit=25) -> np.array:
    x = np.zeros(coefficients.shape[0])
    diag = np.diag(coefficients)
    for i in range(limit):
        prev_x = x.copy()
        x = (free_terms - (coefficients - np.diagflat(diag)) @ x) / diag
        if np.allclose(x, prev_x, threshold):
            break
    return x

def main():
    # Task 1
    print(array_filter(np.array([[1, 2], [3, 4]]), 2))

    # Task 2
    print(get_queen_moves(2, 3))

    # Task 3
    chessboard = np.zeros((8, 8), dtype=int)
    chessboard[3, 2] = 1
    chessboard[6, 1] = 1
    print(check_queen_puzzle(chessboard))

    # Task 4
    linear_system = generate_system(np.array([[1], [2]]))
    print(linear_system)

    # Task 5
    print(solve_system(*linear_system))

    # Task 6
    print(np.linalg.solve(*linear_system))
    print(np.linalg.inv(linear_system[0]).dot(linear_system[1]))
    print(all([1, 0, 1]))

if __name__ == "__main__":
    main()


# Online Bayesian linear regression using Kalman Filter
# Based on: https://github.com/probml/pmtk3/blob/master/demos/linregOnlineDemoKalman.m
# Author: Gerardo Durán-Martín (@gerdm)

import superimport

import numpy as np
import matplotlib.pyplot as plt
import pyprobml_utils as pml
from numpy.linalg import inv

def kf_linreg(X, y, sigma2, mu0, Sigma0, A):
    """
    Online estimation of a linear regression
    using Kalman Filters

    Parameters
    ----------
    X: array(n_obs, dimension)
        Matrix of features
    y: array(n_obs,)
        Array of observations
    sigma2: float
        Known variance
    mu0: array(dimension)
        Prior mean
    Sigma0: array(dimesion, dimension)
        Prior covariance matrix

    Returns
    -------
    * array(n_obs, dimension)
        Online estimation of parameters
    * array(n_obs, dimension, dimension)
        Online estimation of uncertainty
    """
    n_obs, dim = X.shape
    mu_hist = np.zeros((n_obs, dim))
    Sigma_hist = np.zeros((n_obs, dim, dim))

    I = np.eye(dim)
    Sigma_t = Sigma0.copy()
    mu_t = mu0.copy()

    mu_hist[0] = mu_t
    Sigma_hist[0] = Sigma_t
    for t in range(n_obs):
        xt, yt = X[t], y[t]
        Kt = inv(inv(Sigma_t) + np.outer(xt, xt) / sigma2) @ xt / sigma2
        mu_t = A @ mu_t + Kt * (yt - xt @ mu_t)
        Sigma_t = (I - Kt[:, None] @ xt[None, :]) @ Sigma_t

        mu_hist[t] = mu_t
        Sigma_hist[t] = Sigma_t

    return mu_hist, Sigma_hist

def posterior_lreg(X, y, sigma2, mu0, Sigma0):
    """
    Compute mean and covariance matrix of a
    Bayesian Linear regression

    Parameters
    ----------
    X: array(n_obs, dimension)
        Matrix of features
    y: array(n_obs,)
        Array of observations
    sigma2: float
        Known variance
    mu0: array(dimension)
        Prior mean
    Sigma0: array(dimesion, dimension)
        Prior covariance matrix

    Returns
    -------
    * array(dimension)
        Posterior mean
    * array(n_obs, dimension, dimension)
        Posterior covariance matrix
    """
    Sn_bayes_inv = inv(Sigma0) + X.T @ X / sigma2
    Sn_bayes = inv(Sn_bayes_inv)
    mn_bayes = Sn_bayes @ (inv(Sigma0) @ mu0 + X.T @ y / sigma2)

    return mn_bayes, Sn_bayes

if __name__ == "__main__":
    plt.rcParams["axes.spines.right"] = False
    plt.rcParams["axes.spines.top"] = False

    n_obs = 21
    timesteps = np.arange(n_obs)
    x = np.linspace(0, 20, n_obs)
    X = np.c_[np.ones(n_obs), x]
    A = np.eye(2)
    mu0 = np.zeros(2)
    Sigma0 = np.eye(2) * 10.

    sigma2 = 1
    # Data from original matlab example
    y = np.array([2.4865, -0.3033, -4.0531, -4.3359, -6.1742, -5.604, -3.5069, -2.3257, -4.6377,
          -0.2327, -1.9858, 1.0284, -2.264, -0.4508, 1.1672, 6.6524, 4.1452, 5.2677, 6.3403, 9.6264, 14.7842])

    # Online estimation
    mu_hist, Sigma_hist = kf_linreg(X, y, sigma2, mu0, Sigma0, A)
    kf_var = Sigma_hist[-1, [0, 1], [0, 1]]
    w0_hist, w1_hist = mu_hist.T
    w0_err, w1_err = np.sqrt(Sigma_hist[:, [0, 1], [0, 1]].T)

    # Offline estimation
    (w0_post, w1_post), Sigma_post = posterior_lreg(X, y, sigma2, mu0, Sigma0)
    w0_std, w1_std = np.sqrt(Sigma_post[[0, 1], [0, 1]])

    # Asserting values for means and variance
    assert np.allclose(w0_hist[-1], w0_post)
    assert np.allclose(w1_hist[-1], w1_post)
    assert np.allclose(w0_err[-1], w0_std)
    assert np.allclose(w1_err[-1], w1_std)

    fig, ax = plt.subplots()
    ax.errorbar(timesteps, w0_hist, w0_err, fmt="-o", label="$w_0$", color="black", fillstyle="none")
    ax.errorbar(timesteps, w1_hist, w1_err, fmt="-o", label="$w_1$", color="tab:red")

    ax.axhline(y=w0_post, c="black", label="$w_0$ batch")
    ax.axhline(y=w1_post, c="tab:red", linestyle="--", label="$w_1$ batch")

    ax.fill_between(timesteps, w0_post - w0_std, w0_post + w0_std, color="black", alpha=0.4)
    ax.fill_between(timesteps, w1_post - w1_std, w1_post + w1_std, color="tab:red", alpha=0.4)

    plt.legend()
    ax.set_xlabel("time")
    ax.set_ylabel("weights")
    ax.set_ylim(-8, 4)
    ax.set_xlim(-0.5, n_obs)

    pml.savefig("linreg-online-kalman.pdf")
    plt.show()


import numpy as np
import linalg_toolbox as linalg_tools

def get_projection_matrix(data, ndim, whiten=False):
    """
    Performs a PCA over the data and return the projection matrix.
    data: the data to be projected. Datapoints are given by rows while features
          are given by columns.
    ndim: the dimension of the lower dimentional subspace the data should be
          projected on.
    """
    n_datapoints, n_features = data.shape
    assert ndim <= n_features, 'We can\'t project onto a higher dimentional space.'

    St = compute_St(data)

    # Getting the eigen vectors (they correspond to the projection matrix we are
    # looking for).
    projection_matrix = linalg_tools.get_largest_eigen_vectors(St, ndim)
    if whiten:
        projection_matrix = linalg_tools.normalize_data(projection_matrix.T).T
    return projection_matrix

def project_using_projection_matrix(data_to_transform, projection_matrix):
    """
    Projects given data into lower dimentional subspace using the provided
    projection_matrix.
    """
    projected_data = data_to_transform * projection_matrix;
    return projected_data

def project(data, ndim, whiten=False):
    """
    Projects an array of data onto a lower dimentional subspace.
    The procedure here is the 'naive' one (as seen in the lectures).
    data: the data to be projected. Datapoints are given by rows while features
          are given by columns.
    ndim: the dimension of the lower dimentional subspace the data should be
          projected on.
    """
    projection_matrix = get_projection_matrix(data, ndim, whiten)
    return project_using_projection_matrix(data, projection_matrix)

def compute_St(data):
    """
    Given a dataset, computes the variance matrix of its features.
    """
    n_datapoints, n_features = data.shape

    # Computing the 'mean image'. A pixel at position (x,y) in this image is the
    # mean of all the pixels at position (x,y) of the images in the dataset.
    # This corresponds to the 'mu' we have seen in the lectures.
    mu = data.mean(axis=0) # apply along the rows for each columns.
    centered_data = data - mu

    # Computing the covariance matrix
    St = (1. / n_datapoints) * (centered_data.T * centered_data)
    return St


from pylab import *
import numpy as np
import matplotlib.pyplot as plt
from matplotlib import animation

c = 9
m = 1
g = 9.81
L = g / c

def harmonicAcceleration(theta):
    return -c * theta

def pendulumAcceleration(theta):
    return -c * np.sin(theta)

def harmonicEnergy(theta, dTheta):
    return 0.5 * m * L**2 * dTheta**2 + 0.5 * m * g * L * theta**2

def pendulumEnergy(theta, dTheta):
    return 0.5 * m * L**2 * dTheta**2 + m * g * L * (1. - np.cos(theta))

def velocityVerlet(A, E, dt, theta, dTheta):
    a = A(theta)
    theta += dTheta * dt + 0.5 * a * dt * dt
    aNext = A(theta)
    dTheta += 0.5 * (aNext + a) * dt

    energy = E(theta, dTheta)
    return theta, dTheta, energy

def findPeriod(A, E, dt, initialTheta, initialDTheta):
    time = 0
    theta = initialTheta
    dTheta = initialDTheta

    def iterateUntilZero():
        nonlocal time, theta, dTheta
        if A(theta) == 0 and abs(dTheta) < 1e-10 and abs(theta) < 1e-10:
            return
        currentSign = sign(theta)
        while(sign(theta) == currentSign):
            time += dt
            theta, dTheta, energy = velocityVerlet(A, E, dt, theta, dTheta)

    iterateUntilZero()
    startTime = time
    iterateUntilZero()
    iterateUntilZero()
    endTime = time

    period = endTime - startTime
    return period

initialValues = np.linspace(0.001, np.pi/2, 25)
harmonicPeriods = []
pendulumPeriods = []
perturbationPeriods = []
for value in initialValues:
    harmonicPeriods.append(findPeriod(harmonicAcceleration,
                                      harmonicEnergy, 0.001, value, 0))
    pendulumPeriods.append(findPeriod(pendulumAcceleration,
                                      pendulumEnergy, 0.001, value, 0))
    perturbationPeriods.append(
        2*np.pi * np.sqrt(1/c) * (1 + value**2/16 + 11*value**4/3072) + 173*value**6/737820)

plt.figure()
plt.plot(np.divide(initialValues, np.pi), np.array(harmonicPeriods))
plt.plot(np.divide(initialValues, np.pi), np.array(pendulumPeriods))
plt.plot(np.divide(initialValues, np.pi), np.array(perturbationPeriods))
plt.figlegend(('Harmonic oscillator', 'Pendulum',
               'Perturbation series (to power 6)'))
plt.xlabel("θ(0)/pi")
plt.ylabel("T (s)")
plt.savefig("./plots/1_2/period.png", dpi=120)


# -*- coding: utf-8 -*-
import numpy as np

import scipy
import scipy.linalg
import scipy.optimize
import scipy.spatial

def vector(x, y, z):
    """ A shortcut for creating 3D-space vectors;
    in case you need a lot of manual np.array([...]) """
    return np.array([x, y, z])

def deg2rad(deg):
    """ Convert degrees (input) to radians """
    return deg*np.pi/180.

def rad2deg(rad):
    """ convert radians (input) to degrees """
    return rad*180./np.pi

def norm(vector):
    """ a shortcut to scipy.linalg.norm() """
    return scipy.linalg.norm(vector)

def unit_vector(vector):
    """ Returns a vector of magnitude 1 with the same direction"""
    return vector / norm(vector)

def angle_between(v1, v2):
    """ Returns the angle between vectors 'v1' and 'v2', in radians:

    >>> angle_between((1, 0, 0), (0, 1, 0))
    1.5707963267948966
    >>> angle_between((1, 0, 0), (1, 0, 0))
    0.0
    >>> angle_between((1, 0, 0), (-1, 0, 0))
    3.141592653589793

    Kudos: https://stackoverflow.com/questions/2827393/
    """
    v1_u = unit_vector(v1)
    v2_u = unit_vector(v2)

    return np.arccos(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))

def arbitrary_rotation_matrix(axis, theta):
    """
    Return the rotation matrix associated with counterclockwise rotation about
    the given axis by theta radians.
    # Kudos to
    # https://stackoverflow.com/questions/6802577/rotation-of-3d-vector
    #import math
    #
    # axis = np.asarray(axis)
    # axis = axis / math.sqrt(np.dot(axis, axis))
    # a = math.cos(theta / 2.0)
    # b, c, d = -axis * math.sin(theta / 2.0)
    # aa, bb, cc, dd = a * a, b * b, c * c, d * d
    # bc, ad, ac, ab, bd, cd = b * c, a * d, a * c, a * b, b * d, c * d
    # return np.array([[aa + bb - cc - dd, 2 * (bc + ad), 2 * (bd - ac)],
    #                  [2 * (bc - ad), aa + cc - bb - dd, 2 * (cd + ab)],
    #                  [2 * (bd + ac), 2 * (cd - ab), aa + dd - bb - cc]])

    # Also Kudos to the guy with another answer for the same question (used here): """
    return scipy.linalg.expm(np.cross(np.eye(3), axis/norm(axis)*theta))

def arbitrary_rotation(point, axis, theta, origin):
    """ Rotate a point around any axis given by axis by angle theta [radians] """

    rotated_point = np.dot(arbitrary_rotation_matrix(axis, theta), point - origin)
    return rotated_point + origin

def rotate(point, angle, axis='x'):
    """ Rotate a point around a given axis by specified angle """
    if axis == 'y':
        axis = vector(0, 1, 0)
    elif axis == 'z':
        axis = vector(0, 0, 1)
    elif axis == 'x':
        axis = vector(1, 0, 0)
    else:
        raise ValueError("Rotation axis should be either 'x', 'y', or 'z' ")

    return arbitrary_rotation(point, axis, angle, vector(0, 0, 0))

def to_polar(point, axis='z'):
    """ Convert (x, y, z) point to (radius, angle, height);
    the axis of the new polar coordinate system can be chosen ('x' or 'z') """

    assert axis in ['x', 'z']

    if axis == 'z':
        radius = (point[0]**2 + point[1]**2)**0.5
        angle = np.arctan2(point[1], point[0])
        height = point[2]
    else: # axis == 'x'
        radius = (point[1]**2 + point[2]**2)**0.5
        angle = np.arctan2(point[2], point[1])
        height = point[0]

    return vector(radius, angle, height)

def to_cartesian(p, direction=1, axis='z'):
    """ Converts a point given in (r, theta, z) coordinates to
    cartesian coordinate system.

    optionally, axis can be aligned with either cartesian axis x* or z and
    rotation sense can be inverted with direction=-1

    *when axis is 'x': theta goes from 0 at y-axis toward z-axis

    """
    assert direction in [-1, 1]
    assert axis in ['x', 'z']

    radius = p[0]
    angle = direction*p[1]
    height = p[2]

    if axis == 'z':
        return vector(radius*np.cos(angle), radius*np.sin(angle), height)

    # axis == 'x'
    return vector( height, radius*np.cos(angle), radius*np.sin(angle) )

def lin_map(x, x_min, x_max, out_min, out_max, limit=False):
    """ map x that should take values from x_min to x_max
        to values out_min to out_max"""
    r = float(x - x_min) * float(out_max - out_min) / \
        float(x_max - x_min) + float(out_min)

    if limit:
        return sorted([out_min, r, out_max])[1]
    else:
        return r

def xy_line_intersection(p_1, p_2, p_3, p_4):
    """ p_1 and p_2 define the first line, p_3 and p_4 define the second;
        return a point of intersection between these two lines in x-y plane

        Kudos: https://en.wikipedia.org/wiki/Line%E2%80%93line_intersection#Given_two_points_on_each_line
    """
    # only take x and y coordinates
    x1 = p_1[0]
    y1 = p_1[1]

    x2 = p_2[0]
    y2 = p_2[1]

    x3 = p_3[0]
    y3 = p_3[1]

    x4 = p_4[0]
    y4 = p_4[1]

    def det(p1, p2, p3, p4):
        return np.linalg.det(np.array([[p1, p2], [p3, p4]]))

    Dx1 = det(x1, y1, x2, y2)
    Dx2 = det(x1,  1, x2,  1)
    Dx3 = det(x3, y3, x4, y4)
    Dx4 = det(x3,  1, x4,  1)
    Dx5 = Dx2
    Dx6 = det(y1,  1, y2,  1)
    Dx7 = Dx4
    Dx8 = det(y3,  1, y4,  1)

    # x-coordinate
    Px = det(Dx1, Dx2, Dx3, Dx4)/det(Dx5, Dx6, Dx7, Dx8)

    # y-coordinate
    Dy1 = Dx1
    Dy2 = Dx6
    Dy3 = Dx3
    Dy4 = Dx8
    Dy5 = Dx2
    Dy6 = Dx6
    Dy7 = Dx7
    Dy8 = Dx8

    Py = det(Dy1, Dy2, Dy3, Dy4)/det(Dy5, Dy6, Dy7, Dy8)
    return vector(Px, Py, 0)

    # alternative solution with vectors
    # A = np.array([
    #     [p_2[0] - p_1[0], p_4[0] - p_3[0]],
    #     [p_2[1] - p_1[1], p_4[1] - p_3[1]],
    # ])
    #
    # b = np.array([p_3[0] - p_1[0], p_3[1] - p_1[1]])
    #
    # k1k2 = np.linalg.solve(A, b)
    # k1 = k1k2[0]
    # k2 = k1k2[1]
    #
    # va = vector(
    #     p_1[0] + k1*(p_2[0] - p_1[0]),
    #     p_1[1] + k1*(p_2[1] - p_1[1]),
    #     0
    # )
    #
    # vb = vector(
    #     p_3[0] + k2*(p_4[0] - p_3[0]),
    #     p_3[1] + k2*(p_4[1] - p_3[1]),
    #     0
    # )
    #
    # print(P-va, P-vb, norm(va-vb))
    # return va

def extend_to_y(p_1, p_2, y):
    """ Return a point that lies on a line defined by p_1 and p_2 and on y=y; only in xy-plane! """
    fk_3 = lambda k: p_1[1] + k*(p_2 - p_1)[1] - y
    k_3 = scipy.optimize.newton(fk_3, 0)

    return p_1 + k_3*(p_2 - p_1)

def arc_length_3point(A, B, C):
    """ Returns length of arc defined by 3 points, A, B and C; B is the point in between """
    A = np.asarray(A)
    B = np.asarray(B)
    C = np.asarray(C)

    t = C - A
    D = A + t/2
    h = B - D

    h = norm(h)
    t = norm(t)

    if h == 0:
        return 0

    fphi = lambda phi: 2*h*np.sin(phi/2) / (1 - np.cos(phi/2)) - t
    phi = scipy.optimize.newton(fphi, 1)
    r = h / (1-np.cos(phi/2))

    return r*phi


# Test this function to perform 'n' Bernoulli trials,
# with function: perform_bernoulli_trials(n, p),
# which returns the number of successes out of n Bernoulli trials,
# each of which has a probability 'p' of success.

# Think of a Bernoulli trial as a flip of a possibly biased coin.
# Specifically, each coin flip has a
# probability 'p' of landing on heads (success)
# probability '1−p' of landing tails (failure)

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

def perform_bernoulli_trials(n, p):
    """Perform n Bernoulli trials with
        probability p (success) and return number of successes"""
    # Initialize number of successes: n_success
    # Start with 0 items in the array, it's going to be filled-up
    # One call to the function will do a loop 100 times
    # Note: it's a 'numpy'-array, so this function returns a 'numpy'-array

    n_success = []
    # print('Type of \'n_success\': ', type(n_success))

    # print('Before the has started, list is', n_success)

    # Perform trials
    for i in range(n):
        # Choose random number between zero and one
        random_number = np.random.random()

        # If less than 'p', store a 'True' as 1 into 'n_success'-list
        if random_number < p :
            # print('Loop', i + 1, 'of', n, '| p:',
            #       p, '| Randomly chosen number:',  round(random_number, 5))
            n_success.append(1)

    # print('After the for-loop (\'for i in range(n)\'): ', n_success)

    return n_success

# ============================================================================

# Seed random number generator
np.random.seed(100)

# Initialize the number of defaults: n_defaults
n_defaults = np.empty(1000, dtype = np.int)

# print(perform_bernoulli_trials(100, 0.05))

# Compute the number of defaults
for i in range(1000):
    n_defaults[i] = np.int(len(perform_bernoulli_trials(100, 0.05)))

# print('Defaults':', n_defaults)

# Compute bin edges: bins

# In a histogram, the total range of data set (i.e from minimum value to maximum value)
# is divided into 8 to 15 equal parts. These equal parts are known as bins or class intervals.
bins = np.arange(0, max(n_defaults) + 1.5) - 0.5

# Plot the histogram with default number of bins; label your axes
_ = plt.title('Probability according Bernoulli trials')
_ = plt.hist(n_defaults, density = True, bins = bins)
_ = plt.xlabel('Number of \'defaults\' (\'in gebreke zijnde\' ) out of 100 provided loans')
_ = plt.ylabel('Probability')

# Show the plot
plt.show()