"""Analyze GHCN data.

"""
# pylint: disable=invalid-name, locally-disabled
# pylint: disable=pointless-string-statement, locally-disabled

import numpy as np
import matplotlib.pyplot as plt

from sklearn.preprocessing import PolynomialFeatures
from sklearn.linear_model import LinearRegression

__author__ = "Phil Beffrey"
__copyright__ = "Copyright (c) 2019, Phil Beffrey"
__license__ = "MIT"
__version__ = "0.0.1"

def linear_regression(x, y):
    """Fit the {x, y} data with a line.

    """
    x_1 = np.c_[np.ones((len(x), 1)), x]  # add x0 = 1 to each instance
    z = np.linalg.inv(x_1.T.dot(x_1)).dot(x_1.T).dot(y)
    # print(z)

    x_new = np.array([[0], [len(x)]])
    x_new_1 = np.c_[np.ones((2, 1)), x_new]
    y_predict = x_new_1.dot(z)
    # print(y_predict)
    return {'x': x_new, 'y': y_predict}

def polynomial_regression(x, y, deg):
    """Fit the {x, y} data with a curve.

    """
    poly_features = PolynomialFeatures(degree=deg, include_bias=False)
    x_poly = poly_features.fit_transform(x)

    lin_reg = LinearRegression()
    lin_reg.fit(x_poly, y)

    length = len(x)
    x_new = np.linspace(0, length - 1, length).reshape(length, 1)
    x_new_poly = poly_features.transform(x_new)
    y_new = lin_reg.predict(x_new_poly)
    return {'x': x_new, 'y': y_new}

PLOT_LAYOUT = []
PLOT_LAYOUT_INDEX = 0

def plot_years(df, id_, fit_fn, years=None):
    """Plot selected years for a station.

    """
    global PLOT_LAYOUT_INDEX
    if PLOT_LAYOUT:
        ax = PLOT_LAYOUT[PLOT_LAYOUT_INDEX]
        PLOT_LAYOUT_INDEX = (PLOT_LAYOUT_INDEX + 1) % len(PLOT_LAYOUT)
    else:
        ax = plt

    colors = ['b', 'r', 'g', 'c', 'm', 'y']
    n_plots = 0
    x = df.index.values
    for y in years:
        c = colors[n_plots % len(colors)]
        n_plots += 1
        y = df[y]
        ax.plot(x, y, snap=False, alpha=0.20, color=c)
        """
        result = linear_regression(x, y)
        plt.plot(result['x'], result['y'], 'r-')
        """

        # (!?) Sort of the same as: 'X = x.reshape(-1, 1)'
        X = []
        for i in x:
            X.append([i])

        # degree=5 seems about right for yearly TMAX data.
        # (!) But need to pad with data from prev and next years.
        result = fit_fn(X, y, 5)
        ax.plot(result['x'],
                result['y'],
                "r-",
                snap=False,
                alpha=0.35,
                color=c)

    ax.set_title(id_ + ' ' + str(years[0]) + '-' + str(years[len(years) - 1]))
    # plt.show() # Comment out to allow the *next* show() to draw.


import sympy as sym
import numpy as np

V, t, I, w, dt = sym.symbols('V t I w dt')  # global symbols
f = None  # global variable for the source term in the ODE

def ode_source_term(u):
    """Return the terms in the ODE that the source term
    must balance, here u'' + w**2*u.
    u is symbolic Python function of t."""
    return sym.diff(u(t), t, t) + w**2*u(t)

def residual_discrete_eq(u):
    """Return the residual of the discrete eq. with u inserted."""
    R = DtDt(u, dt) + w**2*u(t) - f
    return sym.simplify(R)

def residual_discrete_eq_step1(u):
    """Return the residual of the discrete eq. at the first
    step with u inserted."""
    half = sym.Rational(1,2)
    R = u(t+dt) - I - dt*V - \
        half*dt**2*f.subs(t, 0) + half*dt**2*w**2*I
    R = R.subs(t, 0)  # t=0 in the rhs of the first step eq.
    return sym.simplify(R)

def DtDt(u, dt):
    """Return 2nd-order finite difference for u_tt.
    u is a symbolic Python function of t.
    """
    return (u(t+dt) - 2*u(t) + u(t-dt))/dt**2

def main(u):
    """
    Given some chosen solution u (as a function of t, implemented
    as a Python function), use the method of manufactured solutions
    to compute the source term f, and check if u also solves
    the discrete equations.
    """
    print '=== Testing exact solution: %s ===' % u(t)
    print "Initial conditions u(0)=%s, u'(0)=%s:" % \
          (u(t).subs(t, 0), sym.diff(u(t), t).subs(t, 0))

    # Method of manufactured solution requires fitting f
    global f  # source term in the ODE
    f = sym.simplify(ode_source_term(u))

    # Residual in discrete equations (should be 0)
    print 'residual step1:', residual_discrete_eq_step1(u)
    print 'residual:', residual_discrete_eq(u)

def linear():
    """Test linear function V*t+I: u(0)=I, u'(0)=V."""
    main(lambda t: V*t + I)

def quadratic():
    """Test quadratic function q*t**2 + V*t + I."""
    q = sym.Symbol('q')  # arbitrary constant in t**2 term
    u_e = lambda t: q*t**2 + V*t + I
    main(u_e)

def cubic():
    r, q = sym.symbols('r q')
    main(lambda t: r*t**3 + q*t**2 + V*t + I)

def solver(I, V, f, w, dt, T):
    """
    Solve u'' + w**2*u = f for t in (0,T], u(0)=I and u'(0)=V,
    by a central finite difference method with time step dt.
    f(t) is a callable Python function.
    """
    dt = float(dt)
    Nt = int(round(T/dt))
    u = np.zeros(Nt+1)
    t = np.linspace(0, Nt*dt, Nt+1)

    u[0] = I
    u[1] = u[0] - 0.5*dt**2*w**2*u[0] + 0.5*dt**2*f(t[0]) + dt*V
    for n in range(1, Nt):
        u[n+1] = 2*u[n] - u[n-1] - dt**2*w**2*u[n] + dt**2*f(t[n])
    return u, t

def test_quadratic_exact_solution():
    """Verify solver function via quadratic solution."""
    # Transform global symbolic variables to functions and numbers
    # for numerical computations
    global p, V, I, w
    p, V, I, w = 2.3, 0.9, 1.2, 1.5
    global f, t
    u_e = lambda t: p*t**2 + V*t + I # use p, V, I, w as numbers
    f = ode_source_term(u_e)         # fit source term
    f = sym.lambdify(t, f)           # make function numerical

    dt = 2./w
    u, t = solver(I=I, V=V, f=f, w=w, dt=dt, T=3)
    u_e = u_e(t)
    error = np.abs(u - u_e).max()
    tol = 1E-12
    assert error < tol
    print 'Error in computing a quadratic solution:', error

if __name__ == '__main__':
    linear()
    quadratic()
    cubic()
    test_quadratic_exact_solution()


#This program is used to represent the k-means cluster algorithm as presented in class
import numpy as np
import matplotlib.pyplot as plt

data = np.loadtxt("C:\\Users\David\Documents\CS445\GMM_data_fall2019.txt")
k= 7
len = data.__len__()
centroids = np.zeros((k,2))

#Get random points for the centroids
for i in range(0,k):
 rand_index = np.random.choice(data.__len__(),1)
 centroids[i] = data[rand_index]

#Assigns each point to a cluster
#Goes each datapoint and finds the centroid closest to it
#Places the point in the corresopnding cluster
def get_clusters(data, centroids):
    clusters = [[] for i in range(k)]
    for i in range(0,len):
        min_dist = 1000.0
        for j in range(0,k):
           p1 = centroids[j]
           p2 = data[i]
           dist = np.linalg.norm(p1 - p2)
           if dist < min_dist:
               index = j
               min_dist = dist
        point = data[i]
        clusters[index].append(point)
    return clusters

#Calculates the new_means for each cluster
#Finds the total x and y value for each cluster, then divides each by the length of the cluster
#Returns the list of new means/ centroids
def get_means(clusters):
    new_means = np.zeros((k, 2))

    for i in range(0,k):
        x_tot = 0
        y_tot = 0
        cluster_len = clusters[i].__len__()
        for j in range(0,cluster_len):
            point = clusters[i][j]
            x_tot += point[0]
            y_tot += point[1]

        if cluster_len == 0:
            cluster_len = 1

        new_means[i] = [x_tot/float(cluster_len), y_tot/float(cluster_len)]
    return new_means

#Checks if the difference between the old centroids and the new ones are different
#Returns False if they are
#Returns True if they aren't
#Has a tolerance of .2 in difference
def check_means(centroids, new_means):
    done = True
    for i in range(0,k):
        centroid_x = round(centroids[i][0],2)
        new_means_x = round(new_means[i][0],2)
        centroid_y = round(centroids[i][1],2)
        new_means_y = round(new_means[i][1],2)

        if np.abs(centroid_x-new_means_x) >= .2 or np.abs(centroid_y-new_means_y) >= .2:
           done = False

    return done

done = False
count = 0
#Execute the k-means algorithm until done or 100 times
while not done and count <= 100:
    clusters = get_clusters(data,centroids)
    new_means = get_means(clusters)
    done = check_means(centroids,new_means)
    count += 1
    if not done:
        centroids = new_means

#Handle Plotting
colors = ['r','b','g','y','k','c','m']
#assign each data point to the correct cluster by color for the plot
for i in range(0,k):
    for j in range(0,clusters[i].__len__()):

        plt.scatter(clusters[i][j][0],clusters[i][j][1],15,c=colors[i])

plt.show()


#!/usr/bin/env python
"""
Let's say we play a game where I keep flipping a coin until I get
heads. If the first time I get heads is on the nth coin, then I pay
you 2n-1 dollars. How much would you pay me to play this game?

You should end up with a sequence that you need to find the closed
form of. If you don't know how to do this, write some python code that
sums the first 100.

E(W) = sum_{n >= 1} (2n-1)/2^n = 3

"""

import matplotlib.pyplot as plt
import numpy as np

## simulate the number of flips before heads
def coin():
    tails, num_flips = True, 0
    while tails:
        num_flips += 1
        if np.random.binomial(1,0.5):
            tails = False
    return num_flips

if __name__ == '__main__':

    ## simulate
    flips = [coin() for k in xrange(10000)]

    ## get the distribution of counts condition on the number of flips
    range_flips = range(1, max(flips) + 1)
    counts = np.array([flips.count(k)*1. for k in range_flips])

    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.bar(range_flips,counts,alpha=0.4)
    ax.set_ylabel("counts")
    ax.set_xlabel("num flips to win")

    #print [int(i) for i in counts]

    winnings = sum([counts[k - 1]*(2*(k)-1)/sum(counts) for k in range_flips])

    #print range_flips
    print winnings

    plt.show()


import numpy as np
import matplotlib.pyplot as plt

def data_produce():

    # ----------------
    # 产 生 数 据
    # ----------------

    x1 = np.random.randint(0,1000,50)
    y1 = np.random.randint(0,1000,50)
    x2 = np.random.randint(1000,2000,50)
    y2 = np.random.randint(1000,2000,50)

    x = np.hstack((x1,x2))
    y = np.hstack((y1,y2))

    final_data = []

    for i in range(100):
        data = [x[i],y[i]]
        if(i < 50):
            data_dict = {'data':data,'flag':-1}
        else:
            data_dict = {'data':data, 'flag':1}
        final_data.append(data_dict)

    return final_data

def perceptron(x):

    w = np.ones(2)
    b = 0
    a = 3
    all_true = False

    while(not all_true):
        update_w = 0
        update_b = 0
        wrong_num = 0
        for data_dict in x:
        # ------------
        # 遍历所有的点
        # ------------
            x_data = np.array(data_dict['data'])
            judge = data_dict['flag']*(np.dot(x_data,w) + b)
            if judge < 0:
                wrong_num += 1
                print('wrong:', data_dict['data'])
                update_w += data_dict['flag']*np.array(data_dict['data'])
                update_b += data_dict['flag']

        print('wrong_num',wrong_num)
        if wrong_num == 0:
            all_true = True
            break
        w = w + a*update_w/wrong_num
        b = b + a*update_b/wrong_num
        print('w:',w)
        print('b',b)

    return w,b

if __name__ == "__main__":
    x = data_produce()
    w,b = perceptron(x)
    testx1 = -1000
    testy1 = testx1 * w[0] / (-1 * w[1]) + b/(-1 * w[1])
    testx2 = 3000
    testy2 = testx2 * w[0] / (-1 * w[1]) + b/(-1 * w[1])

    x1 = np.random.randint(0,1000,50)
    y1 = np.random.randint(0,1000,50)
    x2 = np.random.randint(1000,2000,50)
    y2 = np.random.randint(1000,2000,50)
    plt.scatter(x1,y1)
    plt.scatter(x2, y2)
    plt.plot([testx1,testx2],[testy1,testy2])
    plt.show()


import pickle
import numpy as np

def eucl_dist(a, b, axis=1):
    return np.linalg.norm(a - b, axis=axis)

def k_mean(x, k):

    #initalizing cluster variable
    cluster = np.zeros(x.shape[0])

    # calculation min and max for every dimension of data
    minv = np.min(x,axis=0)
    maxv = np.max(x,axis=0)

    # for k in range(2,11):
    error = 0

    # initalizing centroids of k clusters
    center = np.zeros((k, x.shape[1]))
    for i in range(k):
        for j in x.shape[1]:
            center[i,j] = np.random.randint(minv, maxv)

    # assigining zeros to old centroids value
    center_old = np.zeros(center.shape)

    # initial error
    err = eucl_dist(center, center_old, None)

    while err != 0:

        # calculatin distance of data points from centroids and assiging min distance cluster centroid as data point cluster
        for i in range(len(x)):
            distances = eucl_dist(x[i], center)
            clust = np.argmin(distances)
            cluster[i] = clust

        # changing old centroids value
        center_old = np.copy(center)

        # Finding the new centroids by taking the average value
        for i in range(k):
            points = [x[j] for j in range(len(x)) if cluster[j] == i]
            if points:
                center[i] = np.mean(points, axis=0)

        # calculation difference between new centroid and old centroid values
        err = eucl_dist(center, center_old, None)

    # calculation total difference between cluster centroids and cluster data points
    for i in range(k):
        d = [eucl_dist(x[j],center[i],None) for j in range(len(x)) if cluster[j] == i]
        error += np.sum(d)

    # counting data points in all clusters
    count = {key: 0.0 for key in range(k)}
    for i in range(len(x)):
        count[cluster[i]] += 1

    # displaying cluster number, average distance between centroids and data points and cluster count
    print k, error/len(x), count

    return cluster

if __name__ == '__main__':

    # loading dataset of form [[data1],[data2], ....]
    inp = pickle.load(open('test.pickle', 'rb'))
    x = np.array([i[0] for i in inp])

    # return cluster number for every data
    cluster = k_mean(x)


#!/usr/bin/env python

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

def grad(y, x):
    dy = np.diff(y)
    dx = np.diff(x)
    dydx = dy / dx
    return np.hstack([dydx, 0])

def moving_average(a, n=3) :
    ret  = np.cumsum(a, dtype=float)
    ret[n:] = ret[n:] - ret[:-n]
    return ret[n - 1:] / n

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='compute velocity from trajectory')
    parser.add_argument('--avgbins', '-a', type=int, action='store', default=5, required=False,
                        help='number of bins over which to average velocity')
    parser.add_argument('trajectory_file')
    args = parser.parse_args()

    a = int(args.avgbins)

    traj = np.loadtxt(args.trajectory_file)

    x = traj[:,4]
    y = traj[:,8]

    t = traj[:, 0] - traj[0, 0]
    vx = grad(x, t)
    vy = grad(y, t)

    vxa   = np.hstack([np.zeros(a-1), moving_average(vx,a)])
    vya   = np.hstack([np.zeros(a-1), moving_average(vy,a)])
    vabs  = np.sqrt(np.square(vx) + np.square(vy))
    vabsa = np.sqrt(np.square(vxa) + np.square(vya))

    a = np.transpose(np.vstack([t, vabs, vabsa]))

    imax = np.argmax(vabsa)
    print "max avg velocity: %f m/s = %f km/h = %f mph at time: %f" % (vabsa[imax], vabsa[imax]*3.6, vabsa[imax]*2.23694, t[imax])

#    for row in a:
#        print "%f  %f %f" % (row[0], row[1], row[2])


import numpy as np

def relu(x):
    return np.maximum(x, 0)

def relu_derv(x):
    # y = relu(x) is not differentiable at x = 0.
    # if y > 0, derivative = 1
    # otherwise, derivative = 0
    return np.sign(x)

def sigmoid(x):
    return np.exp(-np.maximum(-x, 0)) / (1.0 + np.exp(-np.abs(x)))

def sigmoid_derv(x):
    # derivative of sigmoid.
    return x * (1 - x)

def tanh(x):
    return 2 * sigmoid(2*x) - 1

def tanh_derv(x):
    return (1.0 + x) * (1.0 - x)

def sigmoid_cross_entropy_with_logits(pt, x):
    # pt: p(true)
    # 1-pt : p(false)
    return np.maximum(x, 0) - x * pt + np.log(1 + np.exp(-np.abs(x)))

def sigmoid_cross_entropy_with_logits_derv(pt, x):
    # pt: p(true)
    # 1-pt : p(false)
    return -pt + sigmoid(x)

def softmax(x):
    # x: (batch, class)
    max_elem = np.max(x, axis=1)
    diff = (x.transpose() - max_elem).transpose()
    exp = np.exp(diff)
    sum_exp = np.sum(exp, axis=1)
    probs = (exp.transpose() / sum_exp).transpose()
    return probs

def softmax_cross_entropy_with_logits(labels, logits):
    # H(P,Q) = -SUM p_i log (q_i + eps)
    # eps: epsilon
    eps = 1.0e-10

    probs = softmax(logits)
    return -np.sum(labels * np.log(probs + eps), axis=1)

def softmax_cross_entropy_with_logits_derv(labels, logits):

    return softmax(logits) - labels

def onehot(x, dim_x):
    y = np.eye(dim_x)[np.array(x).astype(int)]
    return y


import numpy as np

def softmax(scores):
    """
    Computes only the softmax activation
    :param scores is a NxD matrix of N scores for D classes
    :return a NxD matrix with the probabilities for each class for each sample
    """
    log_c = -np.max(scores, axis=1)
    scores_better_numeric = np.exp((scores.T + log_c).T)
    sum_scores = np.sum(scores_better_numeric, axis=1)
    probabilities = (scores_better_numeric.T / sum_scores).T

    return probabilities

def softmax_loss(W, x, y, reg=0):
    """
    Computes the softmax loss function.
    """

    num_train = x.shape[0]
    scores = np.dot(x, W.T)
    probabilities = softmax(scores)

    loss = -np.sum(np.log(probabilities[np.arange(num_train), y])) / num_train
    loss += 0.5 * reg * np.sum(W * W)

    probabilities[np.arange(num_train), y] -= 1
    dW = np.dot(probabilities.T, x) / num_train
    dW += reg * W

    return loss, dW

def logistic(scores):
    probabilities = 1.0 / (1.0 + np.exp(-scores))
    return probabilities


import numpy as np

# Calculates the softmax of vector x with given temperature
def softmax(x, temp):
    x = x / temp # scale by temperature
    z = x - np.max(x) # substract max to prevent overflow of softmax
    return np.exp(z)/np.sum(np.exp(z)) # compute softmax


import numpy as np
from scipy import interpolate

class Drone:
    def __init__(self,waypointPositions,waypointSpeeds,numSteps):
        self.waypointPositions = waypointPositions #m
        self.waypointSpeeds = waypointSpeeds #m/s

        self.numSteps = numSteps #This is somewhat accurate. It's the resolution essentially

        #Calculating the time it should take to finish each segment of the set of waypoints
        self.waypointTimes = []
        for i in range(1,len(self.waypointPositions)):
            distance = np.linalg.norm(np.subtract(self.waypointPositions[i],self.waypointPositions[i-1]))
            waypointTime = distance / self.waypointSpeeds[i-1]
            self.waypointTimes.append(waypointTime)

        #Sum of all of the waypoint times is the total time of the mission
        self.totalTime = np.sum(self.waypointTimes)

    def interpolate(self):
        self.totalPos = []
        for i in range(1,len(self.waypointPositions)):

            #Determine how fast each XYZ component should go to follow m/s desired
            posDiff = np.subtract(self.waypointPositions[i],self.waypointPositions[i-1]) #Find difference of pos
            normPosDiff = np.linalg.norm(posDiff) #3D Length of pos diff
            speedMultVals = posDiff / normPosDiff #Pos diff in unit vector terms
            xyzSpeeds = self.waypointSpeeds[i-1] * speedMultVals #Speeds that each component needs to go

            X = [self.waypointPositions[i-1][0],self.waypointPositions[i][0]] #X values of waypoints
            Y = [self.waypointPositions[i-1][1],self.waypointPositions[i][1]] #Y values of waypoints
            Z = [self.waypointPositions[i-1][2],self.waypointPositions[i][2]] #Z values of waypoints

            #Scipy linear interpolation objects of X,Y & Z values
            fx = interpolate.interp1d([0,1],X)
            fy = interpolate.interp1d([0,1],Y)
            fz = interpolate.interp1d([0,1],Z)

            #Calculating number of steps based on total time and waypoint time
            numSteps = self.numSteps * (self.waypointTimes[i-1] / self.totalTime)

            #Linear interpolation
            newX = fx(np.linspace(0,1,num=int(numSteps)))
            newY = fy(np.linspace(0,1,num=int(numSteps)))
            newZ = fz(np.linspace(0,1,num=int(numSteps)))

            #Put in [X,Y,Z] format
            for ii in range(len(newX)):
                self.totalPos.append([newX[ii],newY[ii],newZ[ii]])
            print("Finished waypoint " + str(i-1) + "-" + str(i) + " at step number: " + str(len(self.totalPos)))
        #TODO Maybe add something if amount of scans needs to be exact
        return np.array(self.totalPos)

if __name__ == "__main__":
    waypoints = [[0,0,0],[0,0,5],[5,0,5],[0,0,5],[0,0,0]] #m
    speeds = [1,2,2,1] #m/s
    numSteps = 4200
    x = Drone(waypoints,speeds,numSteps)

    pos = x.interpolate()


# coding=utf-8
#矩阵常用操作
import numpy as np
nd14 = np.arange(9).reshape([3, 3])
#矩阵转置
np.transpose(nd14)
#矩阵乘法运算
a = np.arange(12).reshape([3, 4])
b = np.arange(8).reshape([4, 2])
a.dot(b)
#求矩阵的迹
a.trace()
#计算矩阵行列式
np.linalg.det(nd14)
#计算逆矩阵
c = np.random.random([3, 3])
np.linalg.solve(c, np.eye(3))

'''
diag:以一维数组方式返回方阵对角线元素
dot:矩阵乘法
trace:求迹，即对角线元素的和
det:计算行列式
eig:计算特征值和特征向量
inv:计算方阵的逆
qr:计算qr分解
svd:计算奇异值分解svd
solve:解线性方程组Ax = b,其中A为方阵
lstsq:计算Ax = b的最小二乘解
'''


#!/usr/bin/python3

import matplotlib.pyplot as plt
import numpy as np
import scipy.special

"""
Compute the area of a closed Bezier curve specified by
its control points.
"""
def bezier_area(P):
    R = np.array([[0, 1],
                  [-1, 0]])

    N = P.shape[0]-1
    A = 0
    for ii in range(1, N+1):
        for jj in range(0, ii):
            B = scipy.special.binom(
                N, ii) * scipy.special.binom(N, jj) / scipy.special.binom(2*N, ii+jj)
            D = jj/(ii+jj) - (N-jj)/(2*N-ii-jj)
            S = np.dot(P[ii, :], np.matmul(R, P[jj, :]))
            A += B * D * S

    return A

"""
Compute the approximate area of a Bezier curve by sampling
it in multiple points, and running a shoelace algorithm on
the resulting polygon.
"""
def approx_bezier_area(P, num_samples):
    def shoelace(x, y):
        return 0.5*np.abs(np.dot(x, np.roll(y, 1))-np.dot(y, np.roll(x, 1)))

    def bezier_interp(P, t):
        N = P.shape[0]-1
        val = np.zeros((t.shape[0], 2), dtype='float')
        for ii in range(0, N+1):
            b = scipy.special.binom(N, ii)
            c = b * np.power(t, ii) * np.power(1-t, N-ii)
            val += c[:, np.newaxis] * P[ii, :]
        return val

    N = P.shape[0]-1
    tt = np.linspace(0, 1, num_samples)
    pts = bezier_interp(P, tt)
    return shoelace(pts[:, 0], pts[:, 1])

def main():
    P = np.array([[-0.5, -0.5], [1, -1.8], [1.5, -5],
                 [0.6, -2.3], [1, 1], [-1, 1], [-0.5, -0.5]])

    Approx = approx_bezier_area(P, 100)
    print(f'the approximate area is {Approx}')

    Area = bezier_area(P)
    print(f'the exact area is {Area}')

if __name__ == '__main__':
    main()


import matplotlib.pyplot as plt
import numpy as np
# In this code we can give a prompt value for initial guess
# and also number of iterations
# def forward_diff(x,y):
#
# e=10**(-4)
# a=x
# b=y
#
#  def func(x):
#     return x**3-20
# funa=func(a)
# funb=func(b)
# # print(func(a))
# # print(func(b))
# if abs((b-a)/b)>e:
#     b= b-(funa*(b-a))/(funb-funa)
# return b
#print(forward_diff(0,1))
def newton_rapshon2(fun, p, q, n, e):
    a = p
    b = q
    #i=0
    #while i<n:
    for val in range(0, n):
        funca = fun(a)
        funcb = fun(b)
        if abs(funca) < e:  #                abs((b-a)/b)<e:
            print("Found solution after", val, "iterations")
            return a
        a = a - (funca * (b - a)) / (funcb - funca)
    else:
        print("exceeded maximum iterations")
    return None

fun = lambda x: x ** 3 - 20

result = newton_rapshon2(fun, 2, 3, 20, 10 ** (-4))
print("The value of the root is:", "%.5f" % result)

x = np.linspace(-5, 5, 50)
f = x ** 3 - 20

fig = plt.figure()
ax = fig.add_subplot(1, 1, 1)
ax.spines['left'].set_position('center')
ax.spines['bottom'].set_position('zero')
ax.spines['right'].set_color('none')
ax.spines['top'].set_color('none')
ax.xaxis.set_ticks_position('bottom')
ax.yaxis.set_ticks_position('left')
#plot the function
plt.plot(x, f, 'g', label='f(x)=x^3-20')

plt.xlabel('x')  # naming the x axis
plt.ylabel('f(x)')  # naming the y axis

# giving a title to my graph
plt.legend(loc='upper left')
#plt.title('f(x)=x^3-20')
plt.savefig("Newton_raphson2.png")
plt.show()


#########################
##                     ##
## Irving Gomez Mendez ##
##    May 11, 2021     ##
##                     ##
#########################
import numpy as np
import pandas as pd
import seaborn as sns
import matplotlib.pyplot as plt

from scipy.stats import boxcox, chi2_contingency
from sklearn.linear_model import Ridge, RidgeCV
from sklearn.model_selection import RepeatedKFold
from sklearn.metrics import mean_squared_error
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split
from statsmodels.stats.outliers_influence import variance_inflation_factor

def hist_variable(data, variable, binwidth=None):
    plt.figure(figsize=(10,7.5))
    sns.histplot(data=data, x=variable, stat='density', color='rebeccapurple')
    sns.kdeplot(data=data, x=variable, fill=True, color='rebeccapurple', linewidth=2)

dat = pd.read_csv("bank_additional/bank-additional.csv", sep=';')
dat = dat.rename(columns={'y':'deposit'})
dat = dat.astype({
    'job': 'category',
    'marital': 'category',
    'education': 'category',
    'default': 'category',
    'housing': 'category',
    'loan': 'category',
    'contact': 'category',
    'month': 'category',
    'day_of_week': 'category',
    'poutcome': 'category',
    'deposit': 'category'})

dat = dat[['duration', 'emp.var.rate', 'cons.price.idx', 'cons.conf.idx', 'euribor3m', 'nr.employed']]

# Brief description of the data
dat.describe()

hist_variable(dat, 'duration')

dat = dat[dat['duration'] > 0]

# We can perform a Box-Cox transformation to normalize the duration?
duration_transform, lamb = boxcox(dat['duration'])

lamb

plt.figure(figsize=(10,7.5))
sns.histplot(duration_transform, stat='density', color='rebeccapurple')
sns.kdeplot(duration_transform, fill=True, color='rebeccapurple', linewidth=2)

np.mean(duration_transform)
np.std(duration_transform)

(duration_transform*lamb+1)**(1/lamb)

dat['duration_transform'] = duration_transform

##############################
##                          ##
## Social and economic data ##
##                          ##
##############################
dat_social_economic = dat[['emp.var.rate', 'cons.price.idx', 'cons.conf.idx', 'euribor3m', 'nr.employed']]
np.round(dat_social_economic.describe(),3)

# We check the condition number
np.linalg.cond(dat_social_economic)
# The condition number is too big, we can see that nr.employed has a different scale that the other features.
# It could be better to standardize this data

dat_social_economic_scale = StandardScaler().fit_transform(dat_social_economic)
dat_social_economic_scale = pd.DataFrame(dat_social_economic_scale, columns=dat_social_economic.columns)

np.linalg.cond(dat_social_economic_scale)
# The condition number is low now, but it could mean a moderate to high correlation between variables

# Kendall-Silvey suggestion as follows: there are as many near dependencies among the columns of a data matrix X as
# there are high condition indexes (singular values small relative to p,,). Two points regarding this extension must be emphasized.

# weak dependencies are associated with condition indexes around 5 or 10, whereas moderate to strong
# relations are associated with condition indexes of 30 to 100.

corr_matrix = dat_social_economic_scale.corr()

plt.figure(figsize=(10,7.5))
sns.heatmap(corr_matrix, cmap='Purples')
# It looks like 4 variables, except cons.conf.idx are highly correlated
# Especially euribor3m and nr.employed, we could just select one of these variables

# We calculate the Variance Inflation Factors

#One recommendation is that if VIF is greater than 5, then the explanatory
#variable given by exog_idx is highly collinear with the other explanatory
#variables, and the parameter estimates will have large standard errors
#because of this.

vif = pd.DataFrame()
vif["VIF Factor"] = np.round([variance_inflation_factor(dat_social_economic_scale.values, i) for i in range(dat_social_economic_scale.shape[1])],2)
vif["features"] = dat_social_economic_scale.columns
vif

np.diag(np.linalg.inv(dat_social_economic_scale.T @ dat_social_economic_scale))*4119
np.diag(np.linalg.inv(corr_matrix))

# Let's do the pairplot.

plt.figure(figsize=(5,5))
sns.pairplot(dat_social_economic_scale, kind="reg", corner = True,
    plot_kws={'line_kws':{'color':'orange'}, 'scatter_kws':{'color': 'rebeccapurple'}},
    diag_kws={'color': 'rebeccapurple'})

# We can check all the condition indexes
u, d, vt = np.linalg.svd(dat_social_economic_scale)
v = vt.T

np.round(d[0]/d,2)
# There are two conditions indexes above 10, the rest are below 5. This would mean that there
# are 2 collinearities in the data
phis = v**2/d**2
var_decomp_portions = phis.T/sum(phis.T)

plt.figure(figsize=(10,7.5))
ax = sns.heatmap(var_decomp_portions, cmap='Purples')
ax.xaxis.set_ticklabels(dat_social_economic_scale.columns)
# This suggests a collinearity between the last 3 variables (especially the last 2)
# and another collinearity between the first two variables

import numpy,from scipy,from statsmodels


import numpy as np

def numerical_grad_no_batch(f, x):
    h = 1e-4
    grad = np.zeros_like(x)  # gradient will have the same shape as x

    for idx in range(x.size):
        tmp_val = x[idx]

        x[idx] = float(tmp_val) + h
        fxh1 = f(x)

        x[idx] = float(tmp_val) - h
        fxh2 = f(x)

        grad[idx] = (fxh1 - fxh2) / (2 * h)
        x[idx] = tmp_val
    return grad

def numerical_gradient(f, x):
    if x.ndim == 1:
        return numerical_grad_no_batch(f, x)
    grad = np.zeros_like(x)
    for idx, x_row in enumerate(x):
        grad[idx] = numerical_grad_no_batch(f, x_row)
    return grad

if __name__ == '__main__':
    def simple_func(x):
        if x.ndim == 1:
            return (x ** 2).sum()
        return (x ** 2).sum(axis=1)

    print(f"let's calculate gradient at [-4,4]")
    grad = numerical_grad_no_batch(simple_func, np.array([-4, 4], dtype=float))
    print(f"grad is {grad}")

    x = 4 * np.random.randn(11, 2)
    grad_batch = numerical_gradient(simple_func, x)
    for idx, grad_row in enumerate(grad_batch):
        print(f"gradient of {x[idx]} is {grad_row}")


#!/bin/python3

import numpy as np

def rodrigues(z,r,j):
    """
    Compute the directions about a cone using the rodrigues angle formula.

   `z` is the vector of the central axis of the cone.

   `r` is the radius of the cone that controls the size of the cone.

   `j` is the number of directions in the cone.

    """
    z /= np.linalg.norm(z)
    z0 = np.zeros(3)
    if np.any(z == 0):
        z0[z == 0] = 1
    else:
        z0[0] = 2/z[0]
        z0[1] = -1/z[1]
        z0[2] = -1/z[2]
    z0 /= np.linalg.norm(z0)
    z0 *= r
    z0 = z + z0
    z0 /= np.linalg.norm(z0)
    B = np.cross(z,z0)
    C = np.dot(z,z0)*z
    directions = np.zeros((j,3))
    for i in range(j):
        x = 2*np.pi*(i+1)/j
        directions[i,:] = z0*np.cos(x)+B*np.sin(x)+C*(1-np.cos(x))
    return directions

def generate_equidistributed_points(desired_number, N, hemisphere=False):
    """
    Generate Equidistributed points on a sphere / hemi-sphere.

    `desired_number` is the desired number of equidistributed points on the 2-sphere.

    `N` is the initial number of points that the algorithm will try to generate. If the number of points generated is less than the desired number, the function will increment `N`.

    If `hemisphere` is set to true, it generates points over hemisphere instead of over whole sphere.
    """
    if hemisphere:
        a = 2*np.pi/N
        d = np.sqrt(a)
        M_theta = int(round(np.pi*.5/d))
        d_theta = np.pi*.5/M_theta
    else:
        a = 4*np.pi/N
        d = np.sqrt(a)
        M_theta = int(round(np.pi/d))
        d_theta = np.pi/M_theta
    d_phi = a/d_theta
    points = []
    for i in range(M_theta):
        if hemisphere:
            theta = np.pi * .5 * i / M_theta
        else:
            theta = np.pi * (i + 0.5) / M_theta
        M_phi = int(round(2*np.pi*np.sin(theta)/d_phi))
        for j in range(M_phi):
            phi = 2*np.pi*j/M_phi
            point = [np.sin(theta)*np.cos(phi),np.sin(theta)*np.sin(phi),np.cos(theta)]
            point /= np.linalg.norm(point)
            points.append(point)
    points = np.array(points)
    if points.shape[0] < desired_number:
        return generate_equidistributed_points(desired_number,N+1,hemisphere)
    else:
        return points

def generate_equidistributed_cones(n_cone, cap_radius = 0.1, n_direction_per_cone = 1, hemisphere=False):
    """
    Generate equidistributed cones that are equidistributed about the sphere.

    It first generates equidistributed points, then use the rodrigues angle formula to compute cones about those points.

    `num_directions` is the number of equidistributed directions we want on the sphere.

    `cap_radius` is the radius of the cones we generate (determines the size of each cone).

    `directions_per_cone` is the number of directions we want generated within each cone.

    If `hemisphere` is set to True, it generates points over hemisphere instead of over whole sphere.
    """
    sphere = generate_equidistributed_points(n_cone, n_cone, hemisphere)
    directions = []
    for i in range(n_cone):
        directions.append(sphere[i]/np.linalg.norm(sphere[i]))
        if n_direction_per_cone > 1:
            for direction in rodrigues(sphere[i],cap_radius,n_direction_per_cone-1):
                direction /= np.linalg.norm(direction)
                directions.append(direction)
    return np.array(directions)


from typing import Sequence, Union

import numpy as np
from numba import njit

from numfun.function import Function

# noinspection PyPep8Naming
@njit
def polyfit_jit(x: np.array, y: np.array, n: int, a: float, b: float) -> tuple:
    """Jitted version of polyfit, see polyfit for details.

    :param x: nodes
    :param y: data
    :param n: degree of the polynomial fit
    :param a: left end point of domain
    :param b: right end point of domain
    :return: the output of np.linalg.lstsq()
    """
    m = len(x)
    Tx = np.zeros((m, n + 1))
    Tx[:, 0] = np.ones(m)
    x_map = 2.0 * (x - a) / (b - a) - 1.0
    Tx[:, 1] = x_map
    for k in range(1, n):
        Tx[:, k + 1] = 2.0 * x_map * Tx[:, k] - Tx[:, k-1]

    # TODO: this is not compiling :(
    # Initialize variables for jit:
    c = np.zeros((n+1,))  # noqa
    residuals = np.zeros((1,))  # noqa
    rank = int(0)  # noqa
    singular_values = np.zeros((n+1,))  # noqa

    c, residuals, rank, singular_values = np.linalg.lstsq(Tx, y, rcond=None)

    return c, residuals, rank, singular_values

def polyfit(
        x: np.ndarray,
        y: np.ndarray,
        degree: Union[int, Sequence[int]] = 1,
        domain: Union[None, np.ndarray, Sequence[float]] = None
) -> Function:
    """Least squares polynomial fitting to discrete data with piecewise domain splitting handled.

    :param x:
    :param y:
    :param degree: an array or a double len(degree) = len(domain) - 1
    :param domain: an array to specify where the breakpionts are
    :return:
    """

    if domain is None or len(domain) == 2:
        assert isinstance(degree, int)
        return polyfit_global(x, y, degree, domain)
    if len(domain) > 2:
        n_pieces = len(domain) - 1
        if isinstance(degree, Sequence):  # A list of degrees is passed
            assert n_pieces == len(degree), 'must specify degree for each domain'
            degrees = degree
        else:  # The degree passed is just an integer
            degrees = n_pieces * [int(degree)]
    else:
        raise AssertionError(f'domain = {domain}, domain must be for the form [a, b]')

    all_coefficients = n_pieces * [None]
    for j in range(n_pieces):
        a, b = domain[j], domain[j+1]
        idx = (a <= x) & (x <= b)
        if np.sum(idx) == 0:
            c = np.array([])
        else:
            xj, yj = x[idx], y[idx]
            f = polyfit_global(xj, yj, degree=degrees[j], domain=[a, b])
            c = f.coefficients[0].copy()
        all_coefficients[j] = c

    return Function(coefficients=all_coefficients, domain=domain)

def polyfit_global(
        x: np.ndarray,
        y: np.ndarray,
        degree: int = 1,
        domain: Union[None, Sequence[float], np.ndarray] = None
) -> Function:
    """Degree n least squares polynomial approximation of data y taken on points x in a domain.

    :param x: x-values, np array
    :param y: y-values, i.e., data values, np array
    :param degree: degree of approximation, an integer
    :param domain: domain of approximation
    :return:
    """

    if domain is None:
        domain = [np.min(x), np.max(x)]

    domain = 1.0 * np.array(domain)
    a = domain[0]
    b = domain[-1]
    assert len(x) == len(y), f"len(x) = {len(x)}, while len(y) = {len(y)}, these must be equal"
    assert degree == int(degree), f'degree = {degree}, degree must be an integer'

    n = int(degree)

    # map points to [-1, 1]
    m = len(x)
    x_normalized = 2.0 * (x - a) / (b - a) - 1.0
    # construct the Chebyshev-Vandermonde matrix:
    Tx = np.zeros((m, n+1))
    Tx[:, 0] = np.ones(m)
    if n > 0:
        Tx[:, 1] = x_normalized
        for k in range(1, n):
            Tx[:, k + 1] = 2.0 * x_normalized * Tx[:, k] - Tx[:, k-1]

    # c, residuals, rank, singular_values = polyfit_jit(x, y, n, a, b)
    c, residuals, rank, singular_values = np.linalg.lstsq(Tx, y, rcond=None)

    # Make a function:
    return Function(coefficients=c, domain=domain)

def main() -> None:
    import matplotlib.pyplot as plt
    x = np.linspace(0, 10, 11)
    y = x ** 2
    n = 2
    domain = [0, 10]
    f = polyfit(x, y, n, domain)
    plt.plot(x, y, '.')
    f.plot()

    x = np.linspace(0, 5, 201)
    y = x * (x < 2.0) + x ** 2 * ((x >= 2.0) & (x < 3.0)) + \
        x ** 3 * ((x >= 3.0) & (x < 4.0)) + x ** 4 * (x >= 4.0)
    domain = [0, 1, 2, 3, 4, 5]
    degrees = [0, 1, 2, 3, 4]
    f = polyfit(x, y, degree=degrees, domain=domain)
    f.plot()

if __name__ == '__main__':
    main()


import unittest
from Statistics.Statistics import Statistics
from CSVReader.CSVReader import CSVReader
import numpy as np
from scipy import stats

class MyTestCase(unittest.TestCase):

    test_data = []

    def setUp(self) -> None:
        self.statistics = Statistics()
        data = CSVReader("Tests/Data/Test_Data.csv").data
        for row in data:
            self.test_data.append(float(row['value1']))

    def test_instantiate_calculator(self):
        self.assertIsInstance(self.statistics, Statistics)

    def test_statistic_mean(self):
        self.assertEqual(self.statistics.mean(self.test_data), np.mean(self.test_data))
        self.assertEqual(self.statistics.result, np.mean(self.test_data))

    def test_statistic_mode(self):
        self.assertEqual(self.statistics.mode(self.test_data), stats.mode(self.test_data).mode[0])
        self.assertEqual(self.statistics.result, stats.mode(self.test_data).mode[0])

    def test_statistic_median(self):
        self.assertEqual(self.statistics.median(self.test_data), np.median(self.test_data))
        self.assertEqual(self.statistics.result, np.median(self.test_data))

    def test_statistic_variance(self):
        self.assertEqual(self.statistics.var(self.test_data), round(np.var(self.test_data),9))
        self.assertEqual(self.statistics.result, round(np.var(self.test_data),9))

    def test_statistic_std(self):
        self.assertEqual(self.statistics.std(self.test_data), round(np.std(self.test_data),9))
        self.assertEqual(self.statistics.result, round(np.std(self.test_data),9))

    def test_statistic_z_score(self):
        z_test = self.statistics.z_score(self.test_data)
        z_result = stats.zscore(self.test_data)
        for i in range(len(z_test)):
            self.assertEqual(z_test[i], round(z_result[i],6))

    def test_checkType(self):
        self.assertEqual(self.statistics.check(self.test_data), type(self.test_data))

    def test_randomeseed(self):
        self.assertEqual(self.statistics.randseed(1,10), self.statistics.randseed(1,10))

    def test_randlist(self):
        # print(len(self.test_data))
        data1 = self.statistics.randlist(1, 50, 10)
        data2 = self.statistics.randlist(1, 50, 10)
        for i in range(10):
            self.assertEqual(data1[i],data2[i])
        # print(len(self.test_data))

    def test_randchoose(self):
        # print(len(self.test_data))
        self.assertEqual(self.statistics.randomchoose(self.test_data), self.statistics.randomchoose(self.test_data))

    def test_randnlist(self):
        data1 = self.statistics.randnlist(self.test_data, 10)
        data2 = self.statistics.randnlist(self.test_data, 10)
        for i in range(10):
            self.assertEqual(data1[i], data2[i])

    def test_randnsample(self):
        data1 = self.statistics.randnsample(self.test_data, 10)
        data2 = self.statistics.randnsample(self.test_data, 10)
        for i in range(10):
            self.assertEqual(data1[i], data2[i])

    def test_randinterval(self):
        self.test_data = []
        data = CSVReader("Tests/Data/Test_Data.csv").data
        for row in data:
            self.test_data.append(float(row['value1']))

        m1, left1, right1 = self.statistics.randinterval(self.test_data, 95)
        self.assertEqual(m1, 99.3155)
        self.assertEqual(left1, 100.183310845)
        self.assertEqual(right1, 98.447689155)

    def test_moe(self):
        self.test_data = []
        data = CSVReader("Tests/Data/Test_Data.csv").data
        for row in data:
            self.test_data.append(float(row['value1']))

        moe = self.statistics.marginerror(self.test_data, 95)
        self.assertEqual(moe, 0.867810845)

    def test_cochran(self):
        self.assertEqual(self.statistics.cochran(self.test_data, 95, 0.5), 385)

    def test_samplesze(self):
        self.assertEqual(self.statistics.samplesze(95,0.06,0.41), 1033)

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


import numpy as np

def solve_bandit(bandit, eps=0, steps=1000, ucb=False, c=1):
    """
    Runs a greedy action selection method to get the best
    sequence of actions.
    Args:
        - bandit (KBandit): the k-armed bandit.
        - eps (float): probability of taking a random action.
        - steps (int): number of steps to run the method
        - ucb (boolean): whether or not to use the Upper Confidence Bound
            instead of greedy or epsilon-greedy approaches.
        - c (float): confidence level used when ucb is True.
    Returns:
        - numpy array of size (steps x 2). The first column
        contains the action taken, the second the reward.
    """
    # Initialize estimates and counts
    action_values = np.zeros(bandit.k)
    action_counts = np.zeros(bandit.k)
    results = np.zeros((steps, 2))

    for step in range(steps):
        # Select action according to selected method
        if ucb:
            # Use the Upper Confidence Bound criterion

            # An action not chosen so far is the maximizer by default
            zero_idx = np.where(action_counts == 0)[0]
            if len(zero_idx) > 0:
                action = zero_idx[0]
            else:
                action = np.argmax(action_values + c * np.sqrt(np.log(step + 1)/action_counts))
        else:
            # Use an epsilon-greedy approach
            if np.random.random() < eps:
                action = np.random.randint(bandit.k)
            else:
                action = np.argmax(action_values)

        # Get reward from bandit
        reward = bandit.get_reward(action)
        results[step, :] = [action, reward]

        # Update counts and estimate for the selected action
        action_counts[action] += 1
        action_values[action] += (reward - action_values[action])/action_counts[action]

    return results


'''
Functions for wrapping real numbers into cyclic intervals, especially
angles.

MIT License

Copyright (c) 2018 Standard Cognition

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

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

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

# Scientific
import numpy as np

def wrap(x: float, l: float = 1.0) -> float:
    '''
    Map/wrap x into the interval [0.0, l) with 0.0 and l identified.

    Args:
        x: number to map.
        l: length of mod interval.

    Returns:
        The image of x in [0.0, l).
    '''
    return float(np.mod(x, l))

def wrap_0_to_2pi(x: float) -> float:
    '''
    Map/wrap an angle x into the interval [0.0, 2.0*np.pi) with 0.0 and
    2.0*np.pi identified.

    Args:
        x: angle in radians.

    Returns:
        Angle radians in [0.0, 2.0*np.pi).
    '''
    return wrap(x, 2.0*np.pi)

def wrap_mpi_to_pi(x: float) -> float:
    '''
    Map/wrap an angle x into the interval [-np.pi, np.pi) with -np.pi and
    np.pi identified.

    Args:
        x: angle in radians.

    Returns:
        Angle radians in [0.0, 2.0*np.pi).
    '''
    y = wrap(x, 2.0*np.pi)
    if y <= np.pi:
        return y
    return float(y - 2.0*np.pi)  # y > np.pi

def geodesic_difference(a: float, b: float) -> float:
    '''
    Geodesic difference on the circle between angles in radians.

    Args:
        a: angle in radians.
        b: angle in radians.

    Returns:
        Geodesic difference in radians.
    '''
    a_wrapped = wrap_0_to_2pi(a)
    b_wrapped = wrap_0_to_2pi(b)

    d = a_wrapped - b_wrapped
    d_magnitude = abs(d)
    d_alternate_magnitude = abs(2.0*np.pi - d_magnitude)

    if d_alternate_magnitude < d_magnitude:
        return float(-np.sign(d)*d_alternate_magnitude)
    return d

def geodesic_distance(a: float, b: float) -> float:
    '''
    Geodesic distance on the circle between angles in radians.

    Args:
        a: angle in radians.
        b: angle in radians.

    Returns:
        Geodesic distance in radians.
    '''
    a_wrapped = wrap_0_to_2pi(a)
    b_wrapped = wrap_0_to_2pi(b)

    d = a_wrapped - b_wrapped
    d_magnitude = abs(d)
    d_alternate_magnitude = abs(2.0*np.pi - d_magnitude)

    return min(d_magnitude, d_alternate_magnitude)


#
#  file:  birthday.py
#
#  Simulate the birthday paradox
#
#  RTK, 04-Jun-2020
#  Last update:  04-Jun-2020
#
################################################################

import numpy as np

# Simulate picking two people at random, probability of sharing a birthday
N = 100000
match = 0
for i in range(N):
    a = np.random.randint(0,364)
    b = np.random.randint(0,364)
    if (a == b):
        match += 1
print()
print("Probability of a random match = %0.6f" % (match/N,))
print()

# Simulate people in a room, N tests per M
M = 30
N = 100000
for m in range(2,M+1):
    matches = 0
    for n in range(N):
        match = 0
        b = np.random.randint(0,364,m)
        for i in range(m):
            for j in range(m):
                if (i != j) and (b[i] == b[j]):
                    match += 1
        if (match != 0):
            matches += 1
    print("%2d people: probability of at least one match %0.6f" % (m, matches/N))


import numpy as np
import scipy.fftpack as spfft
import cvxpy as cvx

def min_spacing(mylist):
    """
    Find the minimum spacing in the list.

    Args:
        mylist (list): A list of integer/float.

    Returns:
        int/float: Minimum spacing within the list.
    """
    # Set the maximum of the minimum spacing.
    min_space = max(mylist) - min(mylist)

    # Iteratively find a smaller spacing.
    for item in mylist:
        spaces = [abs(item - item2) for item2 in mylist if item != item2]
        min_space = min(min_space, min(spaces))

    # Return the answer.
    return min_space

def reconstruct_from_signal(angles, signals, error=0, verb=False):
    """
    Reconstruct the signal from the given angles and signals.

    Args:
        angles (list): List of angles.
        signals (list): List of signals.
        error: An error strength in the 2-norm distance.

    Returns:
        list: Reconstructed signal.
    """
    # Find the minimum spacing.
    spacing = min_spacing(angles)
    itt = int(2 * np.pi / spacing)
    # Make the discret Fourier transform matrix.
    idct_matrix = spfft.idct(np.identity(itt), norm='ortho', axis=0)
    # Make the angles integer-valued.
    angles_discrete = [int(angle / spacing) for angle in angles]
    m_vecs = idct_matrix[angles_discrete]
    vx = cvx.Variable(itt)
    objective = cvx.Minimize(cvx.norm(vx, 1))
    constraints = [cvx.norm(m_vecs * vx - signals, 2) <= error]
    prob = cvx.Problem(objective, constraints)
    result = prob.solve(verbose=verb)
    return idct_matrix @ np.squeeze(np.array(vx.value))

def sparse_reconstruct(angles, signals, magnify = 10, error=0, verb=False):
    """
    Reconstruct the signal from the given angles and signals.

    Args:
        angles (list): List of angles.
        signals (list): List of signals.
        error: An error strength in the 2-norm distance.

    Returns:
        list: Reconstructed signal.
    """
    # Find the minimum spacing.
    spacing = min_spacing(angles)
    itt = int(2 * np.pi / spacing)
#    print("length={}".format(itt))
    # Make the discret Fourier transform matrix.
    idct_matrix = spfft.idct(np.identity(itt), norm='ortho', axis=0)
    # Make the angles integer-valued.
    angles_discrete = [int(angle / spacing) for angle in angles]
    m_vecs = idct_matrix[angles_discrete]
    vx = cvx.Variable(itt)
    objective = cvx.Minimize(cvx.norm(vx, 1))
    constraints = [cvx.norm(m_vecs * vx - signals, 2) <= error]
    prob = cvx.Problem(objective, constraints)
    result = prob.solve(verbose=verb)
    fcoefficients = np.squeeze(np.array(vx.value))
    dim_enlarged = itt * magnify
    fcoefficients_enlarged = np.zeros(dim_enlarged)
    for i in range(itt):
        fcoefficients_enlarged[i*magnify] = fcoefficients[i]

    idct_matrix_enlarged = spfft.idct(np.identity(dim_enlarged), norm='ortho', axis=0)

    return idct_matrix_enlarged @ fcoefficients_enlarged


import numpy as np

def MonteCarlo_double(f, g, x0, x1, y0, y1, n):
    """
    Monte Carlo integration of f over a domain g>=0, embedded
    in a rectangle [x0,x1]x[y0,y1]. n^2 is the number of
    random points.
    """
    # Draw n**2 random points in the rectangle
    x = np.random.uniform(x0, x1, n)
    y = np.random.uniform(y0, y1, n)
    # Compute sum of f values inside the integration domain
    f_mean = 0
    num_inside = 0   # number of x,y points inside domain (g>=0)
    for i in range(len(x)):
        for j in range(len(y)):
            if g(x[i], y[j]) >= 0:
                num_inside = num_inside + 1
                f_mean = f_mean + f(x[i], y[j])
    f_mean = f_mean/num_inside
    area = num_inside/(n**2)*(x1 - x0)*(y1 - y0)
    return area*f_mean

def test_MonteCarlo_double_rectangle_area():
    """Check the area of a rectangle."""
    def g(x, y):
        return (1 if (0 <= x <= 2 and 3 <= y <= 4.5) else -1)

    x0 = 0;  x1 = 3;  y0 = 2;  y1 = 5  # embedded rectangle
    n = 1000
    np.random.seed(8)      # must fix the seed!
    I_expected = 3.121092  # computed with this seed
    I_computed = MonteCarlo_double(
        lambda x, y: 1, g, x0, x1, y0, y1, n)
    assert abs(I_expected - I_computed) < 1E-14

def test_MonteCarlo_double_circle_r():
    """Check the integral of r over a circle with radius 2."""
    def g(x, y):
        xc, yc = 0, 0  # center
        R = 2          # radius
        return  R**2 - ((x-xc)**2 + (y-yc)**2)

    # Exact: integral of r*r*dr over circle with radius R becomes
    # 2*pi*1/3*R**3
    import sympy
    r = sympy.symbols('r')
    I_exact = sympy.integrate(2*sympy.pi*r*r, (r, 0, 2))
    print('Exact integral: {:g}'.format(I_exact.evalf()))
    x0 = -2;  x1 = 2;  y0 = -2;  y1 = 2
    n = 1000
    np.random.seed(6)
    I_expected = 16.7970837117376384  # Computed with this seed
    I_computed = MonteCarlo_double(
        lambda x, y: np.sqrt(x**2 + y**2),
        g, x0, x1, y0, y1, n)
    print('MC approximation, {:d} samples: {:.16f}'\
          .format(n**2, I_computed))
    assert abs(I_expected - I_computed) < 1E-15

if __name__ == '__main__':
    test_MonteCarlo_double_rectangle_area()
    test_MonteCarlo_double_circle_r()


import numpy as np

def softmax(x, n_stable=True):
    ''' Compute softmax nonlinearity

    Args:
        x (ndarray): ndarray of inputs over which to take softmax.
        n_stable (bool): Whether to use numerically stable softmax defaults to True.

    Returns:
        ndarray: The softmax over the input.
    '''

    assert(isinstance(x, np.ndarray))
    # numerically stable, but slightly slower
    if n_stable:
        x = x - np.max(x, axis=1)[:, np.newaxis]

    out = np.exp(x)
    y = out / np.sum(out, axis=1)[:, np.newaxis]
    return y

def sigmoid(x):
    ''' Compute logistic sigmoid nonlinearity

    Args:
        x (ndarray): ndarray of inputs over which to take logistic sigmoid.

    Returns:
        ndarray: The logistic sigmoid of the input.
    '''

    assert(isinstance(x, np.ndarray))
    return 1 / (1 + np.exp(-x))

def tanh(x):
    ''' Compute hyperbolic tangent nonlinearity

    Multiplicative constants ensure that if input is zero mean and unit
    variance, the outout will also be zero mean and unit variance.
    Shown by LeCunn http://yann.lecun.com/exdb/publis/pdf/lecun-98b.pdf
    to help back propagation when inputs are z-scored but batch normalization
    is not performed at each layer.

    Args:
        x (ndarray): ndarray of inputs to over which to take hyperbolic tangent.

    Returns:
        ndarray: The hyperbolic tangent of the input.
    '''

    assert(isinstance(x, np.ndarray))
    # constants results in zero mean unit variance in zero mean unit variance out
    return 1.7159 * np.tanh(2*x/3)

def relu(x):
    ''' Compute rectified linear unit nonlinearity

    Args:
        x (ndarray): ndarray of inputs to over which to take ReLU.

    Returns:
        ndarray: The ReLU of the input.
    '''

    assert(isinstance(x, np.ndarray))
    return np.maximum(x, 0)

def swish(x):
    ''' Compute swish nonlinearity

    Activation function reported to perform better than ReLU in deep networks
    by Ramachandran et al. https://arxiv.org/abs/1710.05941

    Args:
        x (ndarray): ndarray of inputs to over which to take swish.

    Returns:
        ndarray: The swish of the input.
    '''

    assert(isinstance(x, np.ndarray))
    return x * sigmoid(x)

ACTIVATIONS = {'softmax': softmax,
               'sigmoid': sigmoid,
               'tanh': tanh,
               'relu': relu,
               'swish': swish}

def softmax_prime(x, y=None):
    ''' Compute derivative of softmax nonlinearity

    Args:
        x (ndarray): ndarray of inputs over which to take softmax derivative.
        y (ndarray): Cached ndarray of softmax(x) used to speed up calculation
            if available defaults to None indicating softmax must be recomputed

    Returns:
        ndarray: The derivative of softmax over the input.
    '''

    assert(isinstance(x, np.ndarray))
    if y is None:
        y = softmax(x)
    return y * (1 - y)

def sigmoid_prime(x, y=None):
    ''' Compute derivative of logistic sigmoid nonlinearity

    Args:
        x (ndarray): ndarray of inputs over which to take sigmoid derivative.
        y (ndarray): Cached ndarray of sigmoid(x) used to speed up calculation
            if available defaults to None indicating sigmoid must be recomputed

    Returns:
        ndarray: The derivative of sigmoid over the input.
    '''

    assert(isinstance(x, np.ndarray))
    if y is None:
        y = sigmoid(x)
    return y * (1 - y)

def tanh_prime(x, y=None):
    ''' Compute derivative of hyperbolic tangent nonlinearity

    Args:
        x (ndarray): ndarray of inputs over which to take tanh derivative.
        y (ndarray): Cached ndarray of tanh(x) used to speed up calculation
            if available defaults to None indicating tanh must be recomputed

    Returns:
        ndarray: The derivative of tanh over the input.
    '''

    assert(isinstance(x, np.ndarray))
    if y is None:
        y = tanh(x)
    # divide by 1.7159^2 to get pure tanh without multiplicative constant
    return 1.1439 * (1 - y * y / 2.9443)

def relu_prime(x, y=None):
    ''' Compute derivative of rectified linear unit nonlinearity

    Args:
        x (ndarray): ndarray of inputs over which to take ReLU derivative.
        y (ndarray): Not used exists to maintain common interface among all
            activation functions defaults to None

    Returns:
        ndarray: The derivative of ReLU over the input.
    '''

    assert(isinstance(x, np.ndarray))
    return (x > 0).astype(float)

def swish_prime(x, y=None):
    ''' Compute derivative of swish nonlinearity

    Args:
        x (ndarray): ndarray of inputs over which to take swish derivative.
        y (ndarray): Cached ndarray of swish(x) used to speed up calculation
            if available defaults to None indicating swish must be recomputed

    Returns:
        ndarray: The derivative of swish over the input.
    '''
    if y is None:
        y = sigmoid(x)
    else:
        # swish activation is x * sigmoid(x) so divide by x to get sigmoid
        y = y/x

    return y + x * sigmoid_prime(x, y=y)

DERIVATIVES = {'softmax': softmax_prime,
               'sigmoid': sigmoid_prime,
               'tanh': tanh_prime,
               'relu': relu_prime,
               'swish': swish_prime}

def cross_entropy(y, t):
    ''' Compute cross entropy loss

    Args:
        y (ndarray): ndarray the predicted distribution.
        t (ndarray): ndarray the target distribution.

    Returns:
        ndarray: The cross entropy loss between y and t.
    '''

    return -np.sum(t * np.log(y))/y.shape[0]

def mean_squared_error(y, t):
    ''' Compute mean squared error

    Args:
        y (ndarray): ndarray the predicted values.
        t (ndarray): ndarray the target values.

    Returns:
        ndarray: The mean squared error between y and t.
    '''

    return np.sum(np.square(t - y))/y.shape[0]

def mean_absolute_error(y, t):
    ''' Compute mean absolute error

    Args:
        y (ndarray): ndarray the predicted values.
        t (ndarray): ndarray the target values.

    Returns:
        ndarray: The mean absolute error between y and t.
    '''

    return np.sum(np.absolute(t - y))/y.shape[0]

LOSSES = {'cross_entropy': cross_entropy,
          'mean_squared_error': mean_squared_error,
          'mean_absolute_error': mean_absolute_error}


#Program wich solves the one-particle (time independent) Schrodinger
#equation for a potential specified in function potential(). This
#example is for the hydrogen atom. This is a translation of the C++
#code for chapter 12 in "Computational Physics", M. H. Jensen
#Translated to Python by Magnar K. Bugge

import numpy
import math
import sys
import computationalLib

#Read output file name from command line argument, abort if none is given
if len(sys.argv) == 2:
    outfilename = sys.argv[1]
else:
    print '\nError: name of output file must be given as command line argument.\n'
    sys.exit(0)

#Function for initialisation of parameters
def initialize():
    r_min = eval(raw_input('Min value of r: '))
    r_max = eval(raw_input('Max value of r: '))
    orb_l = eval(raw_input('Orbital angular momentum (l): '))
    max_step = eval(raw_input('Number of steps: '))

    return r_min,r_max,orb_l,max_step

#Function for outputting results to file
def output():
    outfile = open(outfilename,'w')
    outfile.write('Results:\nr_min = %f\nr_max = %f\nOrbital angular momentum (l) = \
%d\nNumber of steps = %d\nFive lowest eigenvalues:\n' %(r_min,r_max,orb_l,max_step))
    for i in xrange(5):
        outfile.write('%f\n' %(d[i]))

#Potential function (returns value of potential for given r)
def potential(r):

    return -2.0/r

#Here starts the main program

#Read in data from user
r_min,r_max,orb_l,max_step = initialize()

#Initialize constants
step = (r_max - r_min) / max_step
const_2 = -1.0 / step**2
const_1 = -2.0 * const_2
orb_factor = orb_l * (orb_l + 1)

#Calculate array of potential values
V = numpy.zeros(max_step+1,numpy.double)
for i in xrange(max_step+1):
    r = r_min + i*step
    V[i] = potential(r) + orb_factor/r**2 #(include centrifugal term)

#Calculate elements of the matrix to be diagonalized
d = numpy.zeros(max_step,numpy.double) #diagonal elements
e = numpy.zeros(max_step,numpy.double) #off-diagonal elements
z = numpy.zeros((max_step,max_step),numpy.double) #matrix for eigenvectors (only used as a dummy-argument to
                                                  #tqli() in the current version of this program)
for i in xrange(max_step):
    d[i] = const_1 + V[i+1]
    e[i] = const_2

    z[i,i] = 1 #(identity matrix)

#Diagonalize and obtain eigenvalues. The eigenvalues are stored in d.
computationalLib.pylib(cpp=False).tqli(d,e,z)

#Sort the eigenvalues (smallest to largest)
d = numpy.msort(d)

#Output to file
output()


import matplotlib.pyplot as plt
import numpy as np
from scipy.integrate import odeint
from sympy import pprint, Symbol, Function, Derivative, dsolve, solve, diff
import sympy as sp
from sympy.interactive import printing
printing.init_printing(use_latex = True)

f = Function('f');
x = Symbol('x');

# ODE model 1
def model1(y, x):
    dydx = 1 + x -y;
    return dydx;

model = f(x).diff(x) + 1 + x - f(x);
y_general = dsolve(model, f(x));
print('The general solution: ')
pprint(y_general);

y_special = dsolve(model,f(x), ics={f(0):0});
print('The special solution: ')
pprint(y_special);

# solution = solve(y_special.subs([(f(x), 1)]), x)
# print(solution)

# model 1
plt.figure(1)
# Direction field parameters
X_start = -15;
X_end = 15;
N_X_points = 30; # number of points in X direction
Y_start = -15;
Y_end = 15;
N_Y_points = N_X_points; # number of points in Y direction

X, Y = np.meshgrid(np.linspace(X_start, X_end, N_X_points), np.linspace(Y_start, Y_end, N_Y_points));
delta_X = np.ones((N_X_points, N_Y_points)); # direction vector x component
delta_Y = model1(Y, X); # direction vector y component
vector_normalization = np.sqrt(np.power(delta_X, 2) + np.power(delta_Y, 2));
delta_X = delta_X / vector_normalization; # normalized direction vector x component
delta_Y = delta_Y / vector_normalization; # normalized direction vector y component

# Plot direction field
plt.quiver(X, Y, delta_X, delta_Y, angles="xy");#, scale_units='xy', scale = 1);

# Plot integral curves
x_start = -8;
x_end = 12;
y_start = -15;
y_end = 15;
N_int_curves = 15; # number of solutions to plot
x = np.linspace(x_start, x_end, N_X_points * 100); # evaluate solution y(x) at x
y0_vector = np.linspace(y_start, y_end, N_int_curves); # vector of initial solutions
for solution_no in range(N_int_curves):
    y0 = y0_vector[solution_no]; # initial condition
    y = odeint(model1, y0, x, args=());
    plt.plot(x, y)

plt.xlim([X_start, X_end])
plt.ylim([Y_start, Y_end])
plt.xlabel('X axis')
plt.ylabel('Y axis')
plt.grid()
plt.show()

import numpy,from scipy,import sympy,from sympy


# -*- coding: utf-8 -*-
"""Testing the basic mathematical functions.
"""
import numpy as np

import pytest

from typhon import math

class TestCommon:
    """Testing common mathematical functions."""
    def test_integrate_column(self):
        """Test numerical intergration of array elements.

        As no coordinates are given, the function is expected to integrate
        along the indices.
        """
        t = math.integrate_column(np.arange(5))

        assert(np.isclose(t, 8))

    def test_integrate_column_coordinates(self):
        """Test numerical intergration of array elements along coordinates."""
        x = np.linspace(0, 1, 5)
        t = math.integrate_column(np.arange(5), x)

        assert(np.isclose(t, 2))

    def test_interpolate_halflevels(self):
        ref = np.array([0.5, 1.5, 2.5])
        t = math.interpolate_halflevels(np.arange(4))

        assert(np.allclose(t, ref))

    def test_sum_digits(self):
        """Test calculating several digit sums."""
        assert(math.sum_digits(1) == 1)
        assert(math.sum_digits(10) == 1)
        assert(math.sum_digits(100) == 1)
        assert(math.sum_digits(123) == 6)
        assert(math.sum_digits(2**8) == 13)

    def test_nlogspace(self):
        """Test creation of logarithmic spaced array."""
        ref = np.array([1, 10, 100])
        t = math.nlogspace(1, 100, 3)

        assert(np.allclose(t, ref))

    def test_promote_maximally(self):
        """Test copying to high precision dtype."""
        x = np.array(1, dtype='int8')
        t = math.promote_maximally(x)

        assert(t.dtype == 'int64')

    def test_squeezable_logspace(self):
        """Test creation of squeezable logspace."""
        ref = np.array([1, 3.16227766, 100])
        t = math.squeezable_logspace(1, 100, 3, squeeze=0.5)

        assert(np.allclose(t, ref))

    def test_squeezable_logspace_nosqueeze(self):
        """Test creation of non-squeezed logspace.

        Without squeezing, results should be equal to normal ``nlogspace``.
        """
        ref = np.array([1, 10, 100])
        t = math.squeezable_logspace(1, 100, 3)

        assert(np.allclose(t, ref))

    def test_squeezable_logspace_fixpointbounds(self):
        """Test ValueError if fixpoint is out of bounds."""
        with pytest.raises(ValueError):
            math.squeezable_logspace(100, 1, fixpoint=1.1)

    def test_squeezable_logspace_squeezebounds(self):
        """Test ValueError if squeeze is out of bounds."""
        with pytest.raises(ValueError):
            math.squeezable_logspace(100, 1, squeeze=2.01)

class TestArray:
    """Testing array related mathematical functions."""
    def test_argclosest(self):
        x = np.arange(10)

        idx = math.array.argclosest(x, value=5.4)
        assert idx == 5

        idx, val = math.array.argclosest(x, value=5.4, retvalue=True)
        assert idx == 5 and val == 5.


# coding: utf-8

# In[1]:

import numpy as np

# In[2]:

data = np.load('monthdata.npz')
totals = data['totals']
counts = data['counts']

# In[5]:

totals_annual = np.sum(totals, axis=1)
min_total_annual = np.argmin(totals_annual)
print("Row with lowest total preciptiation:")
print(min_total_annual)

# In[6]:

prep_average = np.sum(totals, axis=0)/np.sum(counts, axis=0)
print("Average precipitation in each month:")
print(prep_average)

# In[7]:

place_daily_average = np.sum(totals, axis=1)/np.sum(counts, axis=1)
print("Average precipitation in each city:")
print(place_daily_average)

# In[8]:

totals_quarterly = np.reshape(totals, (totals.shape[0],4,3))
totals_quarterly_sum = np.sum(totals_quarterly, axis=2)
print("Quarterly precipitation totals:")
print(totals_quarterly_sum)


"""
convolution and pooling with numpy
"""

import numpy as np

# Sobel filter for detecting vertical edges
F_ver = np.array([
    [1, 0, -1],
    [2, 0, -2],
    [1, 0, -1]
])

# Sobel filter for detecting horizontal edges
F_hor = np.array([
    [1, 2, 1],
    [0, 0, 0],
    [-1, -2, -1]
])

def out_size(m_in, k, p, s):
    '''
    calculate the outsize of pooling / convolution
    '''
    m_out = (m_in - k + 2*p) / s + 1
    m_floor = np.floor(m_out)

    if not np.isclose(m_out, m_floor):
        # raise ValueError(f'Expected integer as output: {m_out}')
        pass

    return int(m_floor)

def pad2(X, p=0):
    '''
    2d padding with zeros
    '''

    h, w = X.shape

    Z = np.zeros((2*p + h, 2*p + w))
    Z[p:h+p, p:w+p] = X

    return Z

def conv2(X, F=[[1]], p=0, s=[1, 1]):
    '''
    X is a grayscale image of shape (h, w)
    F is a filter of shape (kh, kw)
    p is the padding size
    s is the stride

    The padding size is identical for the height and width. The stride s is a list
    consisting of two elements. The first element s[0] is the stride along the
    height and the second element s[1] is the stride along the width.

    This function throws an error if the filter size, padding, and stride
    are chosen in such a way that the output height or width is not an integer.
    '''

    X = np.array(X)
    F = np.array(F)

    h, w = X.shape
    kh, kw = F.shape
    sh, sw  = s

    h_out = out_size(h, kh, p, sh)
    w_out = out_size(w, kw, p, sw)

    Z = pad2(X, p)

    O = np.zeros((h_out, w_out))

    for r in range(h_out):
        for c in range(w_out):
            O[r, c] = (Z[sh*r:sh*r+kh, sw*c:sw*c+kw] * F).sum()

    return O

def pool2(X, k=[2, 2], s=[1, 1], action=np.min):
    '''
    X is a grayscale image of shape (h, w)
    k is the filter size
    s is the stride

    The filter size k and stride s are lists consisting of two elements.
    The first sizes k[0], s[0] refer to the height and the second sizes
    k[1], s[1] refer to the width.

    This function throws an error if the filter size and stride are chosen
    in such a way that the output height or width is not an integer.
    '''

    X = np.array(X)

    h, w = X.shape
    kh, kw = k
    sh, sw  = s

    h_out = out_size(h, kh, 0, sh)
    w_out = out_size(w, kw, 0, sw)

    O = np.zeros((h_out, w_out))

    for r in range(h_out):
        for c in range(w_out):
            O[r, c] = action(X[sh*r:sh*r+kh, sw*c:sw*c+kw])

    return O

def pad1(X, p=0):
    '''
    1d padding with zeros
    '''

    w = X.shape[0]

    Z = np.zeros(2*p + w)
    Z[p:w+p] = X

    return Z

def conv1(X, F=[1], p=0, s=1):
    '''
    1d convolution
    '''

    X = np.array(X)
    F = np.array(F)

    w = X.shape[0]
    k = F.shape[0]

    out = out_size(w, k, p, s)

    Z = pad1(X, p)

    O = np.zeros(out)

    for i in range(out):
        si = s * i
        O[i] = (Z[si:si+k] * F).sum()

    return O

def pool1(X, k=2, s=1, action=np.max):
    '''
    1 dim pooling
    '''

    X = np.array(X)
    w = X.shape[0]
    out = out_size(w, k, 0, s)
    O = np.zeros(out)

    for i in range(out):
        si = s * i
        O[i] = action(X[si:si+k])

    return O

def augment_array(X: np.ndarray, n=2, axis=-1):
    '''
    X multidimensional array
    n scaling factor
    axis the axis to repeat

    Scale image by factor n, at last axis
    '''

    return X.repeat(n, axis=axis)


import fractions
import numpy as np

def show_matrix(B):
    matrix_print = []
    for i in range(B.shape[0]):
        row = "["
        for j in range(B.shape[1]):
            if B[i, j].denominator == 1:
                row += "%2d    " % (B[i, j].numerator,)
            else:
                row += "%2d/%2d " % (B[i, j].numerator, B[i, j].denominator)
        row += "]"
        matrix_print.append(row)
    return matrix_print

def show(A, B):
    print("")
    print_A = show_matrix(A)
    print_B = show_matrix(B)
    for row_A, row_B in zip(print_A, print_B):
        print(row_A + " " + row_B)

def map_map(f, xss):
    return [list(map(f, xs)) for xs in xss]

A = np.array(map_map(fractions.Fraction,
                 [[6,3,4,5],
                  [1,2,2,1],
                  [2,4,3,2],
                  [3,3,4,2]]), dtype=fractions.Fraction)

def gauss_jordan(A):
    A = A.copy()

    num_rows, num_columns = A.shape

    # identity matrix with the same dimension as A
    E = np.array([[fractions.Fraction(1 if i == j else 0) for i in range(num_rows)] for j in range(num_columns)])

    # a "DSL" to invert the matrix
    def at(i, j):
        return A[i, j]

    def sub(i, j):
        A[i, :] -= A[j, :]
        E[i, :] -= E[j, :]

    def div(i, x):
        A[i, :] /= x
        E[i, :] /= x

    def swap_row(i, j):
        A[i, :], A[j, :] = A[j, :], A[i, :]
        E[i, :], E[j, :] = E[j, :], E[i, :]

    def get_non_zero_at_throu_row_swapping(index):
        for i in range(index, num_rows):
            if at(i, index) != 0:
                break
        else:
            raise ValueError("SINGULAR MATRIX")
        swap_row(index, i)

    # convert the matrix in a upper triangular matrix
    for i in range(0, num_rows):
        show(A, E)
        get_non_zero_at_throu_row_swapping(i)
        div(i, at(i, i))
        for j in range(i + 1, num_rows):
            if at(j, i) != 0:
                div(j, at(j, i))
                sub(j, i)

    # convert the upper triangular matrix into a identity matrix
    for i in range(num_rows - 1, -1, -1):
        show(A, E)
        div(i, at(i, i))
        for j in range(i - 1, -1, -1):
            if at(j, i) != 0:
                div(j, at(j, i))
                sub(j, i)

    show(A, E)

    return E

gauss_jordan(A)


#!/usr/bin/env python
# coding: utf-8

# In[1]:

import numpy as np

# In[12]:

#Trapezoid from book p.204
def Trapezoid(n, f, a, b):
    h = (b-a)/n
    sum1 = (0.5)*(f(a)+f(b))
    for i in range(1,n):
        x = a + i*h
        sum1 = sum1 + f(x)
    sum1 = sum1*h
    return sum1

# In[13]:

def f(x):
    return 1/(np.exp(x**2))
n = 60
a = 0
b = 1
print(f(a))
print(f(b))
I = Trapezoid(n,f,a,b)
print(I)

# In[ ]:


import cv2
import numpy as np
from matplotlib import pyplot as plt
import math

color1 = cv2.imread('images/color1.png')
color2 = cv2.imread('images/color2.png')

# a)

## For color1
(r1Hist, bin) = np.histogram(np.ravel(color1[:, :, 2]), np.arange(257))
(g1Hist, bin) = np.histogram(np.ravel(color1[:, :, 1]), np.arange(257))
(b1Hist, bin) = np.histogram(np.ravel(color1[:, :, 0]), np.arange(257))
(r1PDF, bin) = np.histogram(np.ravel(color1[:, :, 2]), np.arange(257), density=True)
(g1PDF, bin) = np.histogram(np.ravel(color1[:, :, 1]), np.arange(257), density=True)
(b1PDF, bin) = np.histogram(np.ravel(color1[:, :, 0]), np.arange(257), density=True)
plt.figure(1)

plt.subplot(311)
plt.plot(r1Hist, 'r')
plt.title("histograms of the Color1 image")

plt.subplot(312)
plt.plot(g1Hist, 'g')

plt.subplot(313)
plt.plot(b1Hist, 'b')

plt.show()

## For color2
(r2Hist, bin) = np.histogram(np.ravel(color2[:, :, 2]), np.arange(257))
(g2Hist, bin) = np.histogram(np.ravel(color2[:, :, 1]), np.arange(257))
(b2Hist, bin) = np.histogram(np.ravel(color2[:, :, 0]), np.arange(257))
(r2PDF, bin) = np.histogram(np.ravel(color2[:, :, 2]), np.arange(257), density=True)
(g2PDF, bin) = np.histogram(np.ravel(color2[:, :, 1]), np.arange(257), density=True)
(b2PDF, bin) = np.histogram(np.ravel(color2[:, :, 0]), np.arange(257), density=True)
plt.figure(2)

plt.subplot(311)
plt.plot(r2Hist, 'r')
plt.title("histograms of the Color2 image")

plt.subplot(312)
plt.plot(g2Hist, 'g')

plt.subplot(313)
plt.plot(b2Hist, 'b')

plt.show()

def getCDF(arr):
    CDF = np.zeros((3, 256))  # 0: r, 1: g, 2: b
    for i in range(3):
        (PDF, bin) = np.histogram(np.ravel(arr[:, :, i]), np.arange(257), density=True)
        CDF[2 - i] = np.cumsum(PDF)
    return CDF

def histMatch(image, target):
    tCDF = getCDF(target)
    CDF  = getCDF(image)
    width  = np.size(image, axis=0)
    height = np.size(image, axis=1)

    LUT = np.zeros((3, 256))

    for channel in range(3):
        x = 0
        for i in range(0, 256):
            while x < 255 and tCDF[channel, x] < CDF[channel, i]:
                x += 1
            LUT[channel, i] = x

    for channel in range(3):
        for x in range(width):
            for y in range(height):
                image[x, y, 2 - channel] = LUT[channel, math.floor(image[x, y, 2 - channel])]

    return image

matched = histMatch(color1, color2)
plt.figure(3)
plt.title("histogram matched image")
plt.imshow(matched[:, :, ::-1])
plt.show()

(rHist, bin) = np.histogram(np.ravel(matched[:, :, 2]), np.arange(257))
(gHist, bin) = np.histogram(np.ravel(matched[:, :, 1]), np.arange(257))
(bHist, bin) = np.histogram(np.ravel(matched[:, :, 0]), np.arange(257))

plt.figure(4)

plt.subplot(311)
plt.plot(rHist, 'r')
plt.title("histograms of the matched image")

plt.subplot(312)
plt.plot(gHist, 'g')

plt.subplot(313)
plt.plot(bHist, 'b')

plt.show()


from pylab import*
from scipy.io import wavfile

if len(sys.argv) < 2:
    print("Plays a wave file.\n\nUsage: %s filename.wav" % sys.argv[0])
    sys.exit(-1)

file_name = sys.argv[1]

# sampFreq, snd = wavfile.read('../Downloads/TT_soundtrack_-_k13.wav')
# sampFreq, snd = wavfile.read('../Downloads/Yamaha-TG100-Whistle-C5.wav')
sampFreq, snd = wavfile.read(file_name)

snd = snd / (2.**15)

s1 = snd[:,0]

timeArray = arange(0, float(snd.shape[0]), 1)
timeArray = timeArray / sampFreq
timeArray = timeArray * 1000  #scale to milliseconds

plot(timeArray, s1, color='k')
ylabel('Amplitude')
xlabel('Time (ms)')
savefig(file_name+'.png')
# show()

n = len(s1)
p = fft(s1) # take the fourier transform

nUniquePts = ceil((n+1)/2.0)
p = p[0:nUniquePts]
p = abs(p)

p = p / float(n) # scale by the number of points so that
                 # the magnitude does not depend on the length
                 # of the signal or on its sampling frequency
p = p**2  # square it to get the power

# multiply by two (see technical document for details)
# odd nfft excludes Nyquist point
if n % 2 > 0: # we've got odd number of points fft
    p[1:len(p)] = p[1:len(p)] * 2
else:
    p[1:len(p) -1] = p[1:len(p) - 1] * 2 # we've got even number of points fft

freqArray = arange(0, nUniquePts, 1.0) * (sampFreq / n);
plot(freqArray/1000, 10*log10(p), color='k')
xlabel('Frequency (kHz)')
ylabel('Power (dB)')
# show()
# savefig(file_name+'fft.png')


# Getting the numpy module
import numpy as np

# Getting the matplotbib module
import matplotlib.pyplot as plt

# Number of grid points
N = 11

# Domain size
L = 1

# Corresponding grid spacing
h = np.float64(L / (N - 1))

# Iteration number
iterations = 0

# Initializing temperature field
T = np.zeros(N)
T[N-1] = 1.

# Initializing iterated temperature
T_new = np.zeros(N)
T_new[N-1] = 1.

# Error related variables
epsilon = 1.E-8
numerical_error = 1                                                            # Set as 1 in order to satisfy the while loop condition

# Checking the error tolerance
while numerical_error > epsilon:
    # Computing for all interior points
    for i in range(1, N-1):
        T_new[i] = 0.5 * (T[i-1] + T[i+1])

    # Resetting the numerical error and recalculate
    numerical_error = 0
    for i in range(1, N-1):
        numerical_error = numerical_error + abs(T[i] - T_new[i])

    # Iteration advancement and reassignment
    iterations = iterations + 1
    T = T_new.copy()

    # Plotting numerical error
    plt.figure(1)
    plt.plot(iterations, numerical_error, 'ko')
    # plt.pause(0.01)                                                           # Residual plot on fly Ansys Fluent like

plt.show()

# Plotting the results
plt.figure(2)

# Defining the position from indexes
x_dom = np.arange(N) * h

# Plotting the variation with customization
plt.plot(x_dom, T, 'bx--', linewidth=2, markersize=10)

# Displaying the gridlines
plt.grid(True, color='k')

# Labelling and providing a title to the plot
plt.xlabel('Position', size=15)
plt.ylabel('Temperature', size=15)
plt.title('T(x)')
# plt.margins(0)

# Showing the plot on screen
plt.show()


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

batch = torch.nn.BatchNorm2d(2)

def plotVectors(vecs, cols, alpha=1):
    """
    Plot set of vectors.

    Parameters
    ----------
    vecs : array-like
        Coordinates of the vectors to plot. Each vectors is in an array. For
        instance: [[1, 3], [2, 2]] can be used to plot 2 vectors.
    cols : array-like
        Colors of the vectors. For instance: ['red', 'blue'] will display the
        first vector in red and the second in blue.
    alpha : float
        Opacity of vectors

    Returns:

    fig : instance of matplotlib.figure.Figure
        The figure of the vectors
    """
    plt.figure()
    plt.axvline(x=0, color='#A9A9A9', zorder=0)
    plt.axhline(y=0, color='#A9A9A9', zorder=0)

    for i in range(len(vecs)):
        x = np.concatenate([[0,0],vecs[i]])
        plt.quiver([x[0]],
                   [x[1]],
                   [x[2]],
                   [x[3]],
                   angles='xy', scale_units='xy', scale=1, color=cols[i],
                   alpha=alpha)

def matrixToPlot(matrix, vectorsCol=['#FF9A13', '#1190FF']):
    """
    Modify the unit circle and basis vector by applying a matrix.
    Visualize the effect of the matrix in 2D.

    Parameters
    ----------
    matrix : array-like
        2D matrix to apply to the unit circle.
    vectorsCol : HEX color code
        Color of the basis vectors

    Returns:

    fig : instance of matplotlib.figure.Figure
        The figure containing modified unit circle and basis vectors.
    """
    # Unit circle
    x = np.linspace(-1, 1, 100000)
    y = np.sqrt(1-(x**2))

    # Modified unit circle (separate negative and positive parts)
    x1 = matrix[0,0]*x + matrix[0,1]*y
    y1 = matrix[1,0]*x + matrix[1,1]*y
    x1_neg = matrix[0,0]*x - matrix[0,1]*y
    y1_neg = matrix[1,0]*x - matrix[1,1]*y

    # Vectors
    u1 = [matrix[0,0],matrix[1,0]]
    v1 = [matrix[0,1],matrix[1,1]]

    plotVectors([u1, v1], cols=[vectorsCol[0], vectorsCol[1]])

    plt.plot(x1, y1, 'g', alpha=0.5)
    plt.plot(x1_neg, y1_neg, 'g', alpha=0.5)

A = np.array([[3, 7], [5, 2]])
A_norm = (A-A.mean(0))/np.sqrt(A.var(0))

print(f'Unit circle:')
matrixToPlot(np.array([[1, 0], [0, 1]]))
plt.xlim(-1.5, 1.5)
plt.ylim(-1.5, 1.5)
plt.show()

print(f'Unit circle transformed by A:')
matrixToPlot(A)
plt.xlim(-8, 8)
plt.ylim(-8, 8)
plt.show()

print(f'Unit circle transformed by B:')
matrixToPlot(A_norm)
plt.xlim(-1, 1)
plt.ylim(-1, 1)
plt.show()
#
#
U_A, D_A, V_A = np.linalg.svd(A)
print(f"U: {U_A}")
print(f"D: {D_A}")
print(f"V: {V_A}")

U_B, D_B, V_B = np.linalg.svd(A_norm)
print(f"U: {U_B}")
print(f"D: {D_B}")
print(f"V: {V_B}")
#
#
# u1 = [D[0]*U[0,0], D[0]*U[0,1]]
# v1 = [D[1]*U[1,0], D[1]*U[1,1]]
#
# plotVectors([u1, v1], cols=['black', 'black'])
#
# matrixToPlot(A)
#
# plt.text(-5, -4, r"$\sigma_1u_1$", size=18)
# plt.text(-4, 1, r"$\sigma_2u_2$", size=18)
# plt.xlim(-8, 8)
# plt.ylim(-8, 8)
# plt.show()


#!/usr/bin/env python3

import rospy
import numpy as np
import random

random.seed()
g_noise_gain = 0.1

if __name__ == '__main__':
    rospy.init_node('example_eigen_plane_fit')
    rate = rospy.Rate(1.0)

    normal_vec = np.array([[1], [2], [3]])
    rospy.loginfo("creating example noisy, planar data...")
    print("normal: ")
    normal_vec = normal_vec.T
    print(normal_vec)

    normal_vec = normal_vec / (np.linalg.norm(normal_vec))

    print("unit length normal: ")
    normal_vec = normal_vec.T
    print(normal_vec)

    dist = 1.23
    print("plane distance from origin: ")
    print(dist)

    v1 = np.ones((3, 1))
    v2 = np.ones((3, 1))
    Rot_z = np.array([[0, -1, 0], [1, 0, 0], [0, 0, 1]])

    print("Rot_z: ")
    print(Rot_z)

    # ROS_INFO_STREAM
    rospy.loginfo(Rot_z)

    v1 = Rot_z @ normal_vec

    # ROS_INFO_STREAM
    rospy.loginfo(v1.T)

    print("v1: ")
    v1 = v1.transpose()
    print(v1)

    dotprod = v1.dot(normal_vec)
    dotprod2 = v1 @ normal_vec

    print("v1 dot normal: ")
    print(dotprod)
    print("v1.transpose() * normal_vec: ")
    print(dotprod2)
    print("(should be identical)")

    v2 = np.cross(v1, normal_vec.T)
    v2 = v2 / (np.linalg.norm(v2))

    print("v2: ")
    print(v2.T)

    dotprod = v2.dot(normal_vec)

    print("v2 dot normal_vec = ")
    print(dotprod)
    print(" (should be zero)")

    v1 = np.cross(v2, normal_vec.T)

    print("v1 = ")
    print(v1.transpose())
    print(" v1 dot v2 = ")
    print(v1.dot(v2.T))
    print("v1 dot normal_vec = ")
    print(v1.dot(normal_vec))
    print("(these should also be zero)")

    npts = 10
    points_mat = np.ones((3, npts))
    point = np.zeros((3, 1))
    rand_vec = np.ones((2, 1))

    for ipt in range(npts):
        rand_vec = np.random.random((2, 1))
        point = rand_vec[0] * v1.T + rand_vec[1] * v2.T + dist * normal_vec
        #     print(points_mat[:, ipt])
        points_mat[:, ipt] = point.T

    print("random points on plane (in columns): ")
    print(points_mat)

    Noise = np.random.random((3, npts))
    print("noise_gain = " + str(g_noise_gain) + "; edit this as desired")

    points_mat = points_mat + Noise * g_noise_gain
    print("random points on plane (in columns) w/ noise: ")
    print(points_mat)

    rospy.loginfo("starting identification of plane from data: ")

    centroid = np.zeros((3, 1))
    centroid = centroid.T

    mpts, npts = points_mat.shape

    print("matrix has ncols = ")
    print(npts)

    for ipt in range(npts):
        #     print(points_mat[:, ipt])
        centroid = centroid + points_mat[:, ipt]

    centroid = centroid / npts
    print("centroid: ")
    print(centroid)

    points_offset_mat = points_mat
    for ipt in range(npts):
        points_offset_mat[:, ipt] = points_offset_mat[:, ipt] - centroid

    CoVar = np.ones((3, npts))
    CoVar = points_offset_mat @ (points_offset_mat.transpose())
    print("covariance: ")
    print(CoVar)

    es3d = CoVar
    eigenvalues, eigenvectors = np.linalg.eig(es3d)
    evals = np.ones((3, 3))

    print("The eigenvalues of CoVar are:")
    print(eigenvalues.transpose())
    print(" (these should be real numbers, and one of them should be zero) ")
    print("The matrix of eigenvectors, V, is:")
    print(eigenvectors)
    print(" (these should be real-valued vectors) ")

    evals = eigenvalues.real
    print("real parts of evals: ")
    print(evals.transpose())

    min_lambda = evals[0]
    complex_vec = np.ones((3, 1))
    est_plane_normal = np.ones((3, 1))
    complex_vec = eigenvectors[:, 0].T
    est_plane_normal = complex_vec.real
    i_normal = 0

    for ivec in range(3):
        lambda_test = evals[ivec]
        if lambda_test < min_lambda:
            min_lambda = lambda_test
            i_normal = ivec
            est_plane_normal = eigenvectors[:, ivec].T.real

    print("min eval is " + str(min_lambda) + ", corresponding to component " + str(i_normal))

    print("corresponding evec (est plane normal): ")
    print(est_plane_normal.transpose())

    print("correct answer is: ")
    print(normal_vec.transpose())

    est_dist = est_plane_normal.dot(centroid.T)

    print("est plane distance from origin = ")
    print(est_dist)
    print("correct answer is: ")
    print(dist)

    rospy.loginfo("2ND APPROACH b = A*x SOLN")

    ones_vec = np.ones((npts, 1))
    A = points_mat.transpose()
    # print(A)
    # print(ones_vec)
    x_soln = np.linalg.lstsq(A, ones_vec, rcond=-1)[0]
    # x_soln = np.linalg.solve(A, ones_vec)
    print(x_soln)
    dist_est2 = 1.0 / (np.linalg.norm(x_soln))
    x_soln = x_soln * dist_est2
    dist_est2 = 1.0 / (np.linalg.norm(x_soln))

    print("normal vec, 2nd approach: ")
    print(x_soln.transpose())
    print("plane distance = ")
    print(dist_est2)


import nose
import numpy as np
from sympy import Symbol

import traj

traj.VALIDATION_ENABLED = True

def check_fit_acceleration_segments(v_start, v_end, a_max, j_max):
    t = Symbol('t')
    jerk = traj.seven_segment_type4.fit_acceleration_segments(v_start, v_end, a_max, j_max, t)
    acceleration = jerk.integrate(0.0)
    velocity = acceleration.integrate(v_start)

    t_start = velocity.boundaries[0]
    t_end = velocity.boundaries[-1]
    v_start_computed = velocity(t_start)
    v_end_computed = velocity(t_end)

    assert np.isclose(v_start, v_start_computed)
    assert np.isclose(v_end, v_end_computed)
    return jerk, acceleration, velocity

def check_fit(p_start, p_end, v_start, v_end, v_max, a_max, j_max):
    t = Symbol('t')
    jerk = traj.seven_segment_type4.fit(p_start, p_end, v_start, v_end, v_max, a_max, j_max, t)
    acceleration = jerk.integrate(0.0)
    velocity = acceleration.integrate(v_start)
    position = velocity.integrate(p_start)

    t_start = velocity.boundaries[0]
    t_end = velocity.boundaries[-1]
    v_start_computed = velocity(t_start)
    p_start_computed = velocity(0.0)
    v_end_computed = velocity(t_end)
    p_end_computed = position(t_end)

    assert np.isclose(v_start, v_start_computed)
    assert np.isclose(p_start, p_start_computed)
    assert np.isclose(v_end, v_end_computed)
    assert np.isclose(p_end, p_end_computed)
    return jerk, acceleration, velocity, position

def test_fit_acceleration_segments():
    t = Symbol('t')

    # Positive change in velocity; relatively simple case.
    check_fit_acceleration_segments(0.0, 1.0, 1.0, 10.0)

    # Non-zero starting velocity.
    check_fit_acceleration_segments(-0.67, 1.6, 1.0, 10.0)

    # Negative change in velocity.
    check_fit_acceleration_segments(0.0, -1.0, 1.0, 10.0)

    # Not enough time to reach max acceleration, so this should return an acceleration triangle with two segments.
    jerk, _, _ = check_fit_acceleration_segments(0.0, 1.0, 1e6, 10.0)
    assert len(jerk.functions) == 2

    # Keep the same velocity - should return one zero length segment.
    jerk, _, _ = check_fit_acceleration_segments(0.0, 0.0, 0.0, 10.0)
    assert len(jerk.functions) == 1

def test_seven_segment_given_cruising_acceleration():
    t = Symbol('t')

    check_fit(0.0, 30.0, 0.0, 0.0, 3.0, 2.0, 10.0)


import numpy as np

#Module for dimensionality reduction, in the sense that we're given
#a MxN matrix X and want to yield a transformation from that to some
#matrix which is RxN where R is the rank of X

#Given a MxN matrix, this returns a RxM matrix
#by which X may be left-multiplied to obtain a
#rank-R version of X
#This is done by computing the (reduced) SVD of X, U S V^T
#where U is MxK and V is KxN, and then finding
#the number of non-zero singular values (R)
#and truncating so that U is now MxR,
#S is RxR, and V is RxN
#Then we just return U^T
def get_rank_reduction_matrix(X):
    U, s, V = np.linalg.svd(X, full_matrices=False)
    eps = np.finfo(float).eps
    eps *= s.max() * max(X.shape) #Just following the value from np.linalg.matrix_rank
    R = (s >= eps).sum()
    reduced_U = U[:, :R]
    return np.transpose(reduced_U)


import numpy as np
from math import cos, sin

"""
Swaps ith and jth rows of matrix A
"""
def swap_rows(A, i, j):
    ri = A[i].copy()
    A[i] = A[j]
    A[j] = ri
    return
"""
Swaps ith and jth columns of matrix A
"""
def swap_columns(A, i, j):
    ci = A[:,i].copy()
    A[:,i] = A[:,j]
    A[:,j] = ci
    return

"""
The function finds imax = argmax over ii for abs(A_2[ii,i]) (ii=i,...,m-1) and swaps imax and ith row of P, A_2, L
and swaps i and imax columns of L

Input:
i: index of the row in which we are interested
A_2: The A_new matrix which satisfies PA = LA_new
L: The lower triangular matrix
P: Permutation matrix

Output: None
"""
def perform_swap_operation(i, A_2, L, P):
    m = A_2.shape[0]
    #get the max size element
    imax = i
    maxval = abs(A_2[i,i])
    for ii in range(i+1,m):
        if(maxval<abs(A_2[ii,i])):
            maxval = abs(A_2[ii,i])
            imax = ii
#     print("Swapping: ",i,jmax)
    swap_rows(P, i, imax)
    swap_rows(A_2, i, imax)
    """
    Corresponding change in L matrix: swap i and imax row, swap i and imax column
    (This produces the same effect as swapping just first i-1 elements of rows)
    """
    swap_rows(L, i, imax)
    swap_columns(L, i, imax)
    return

"""
The function performs elimination operation to make A_2[i,i] = 1 and A_2[k,i] = 0
for k>i using row subtractions. If A_2[i,i] = 0 then it leaves it intact and returns

Input:
i: index of the row in which we are interested
A_2: The A_new matrix which satisfies PA = LA_new
L: The lower triangular matrix

Output: None
"""
def perform_elimination_operation(i, A_2, L):
    m = A_2.shape[0]
    #if max element in row is 0 then do nothing it means row is L.D.
    if A_2[i,i]*A_2[i,i] < 10e-12:
        return
    #Otherwise perform row subtraction
    for j in range(i+1, m):
#         print("accessing: ", i, j)
        coeff = A_2[j,i] / A_2[i,i]
        A_2[j] = A_2[j] - (coeff * A_2[i])
        L[j,i] = coeff

"""
The function performs Gaussian Elimination to return
P: Permutation matrix, L: Lower triangular matrix with diagonals 1,
D: Diagonal matrix, U: Upper triangular matrix with diagonals 1 (some diagonals zero if rank(A) < n)

Input: A: an mXn matrix with m>=n, in form of numpy array
Output: P, L, D, U as defined above, all in form of numpy arrays
"""
def get_PA_LDU(A):
    #perform LA' = PA
    assert len(A.shape) == 2, "Only 2D matrices are allowed"
    assert A.shape[0] >= A.shape[1], "Only n_rows > n_cols matrices allowed!"

    A = A.astype(float)

    m = A.shape[0] #number of rows of A
    n = A.shape[1] #number of columns of A

    P = np.eye(m) #diagonal matrix
    L = np.eye(m) #diagonal matrix
    A_2 = A.copy() #initializing A'

    """ For every row i, perform following operations:
        -> if A_2[i,i] = 0, swap row i with row j s.t. j is min. index where A_2[j,i] != 0
        -> perform row operations to make A_2[i,j] = 0, j = i+1,i+2...m
    """

    for i in range(n):
        perform_swap_operation(i, A_2, L, P)
        perform_elimination_operation(i, A_2, L)

    """ We got L and P, now we need D and U from A_2 s.t. A_2 = DU.
        Initialize D = zeros(m,m), U = mxn matrix initialized with identity. For every row i, perform following operations:
        If A_2[i,i] = 0
        -> do nothing (i.e. keep that diagonal zero, and that row in U as it is)
        Else:
        -> D[i,i] = A_2[i,i]. U[i]/=A_2[i,i]

    """

    D = np.zeros((m,m))
    U = np.eye(A_2.shape[0], A_2.shape[1])

    for i in range(n):
        if A_2[i,i]*A_2[i,i] < 10e-12:
            continue
        else:
            D[i,i] = A_2[i,i]
            U[i] = A_2[i]/D[i,i]

    return P, L, D, U


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

def euler(t_start, t_end, dt, x_0, u_0, y_0, v_0, ax):

    t = t_start

    tary = [t]
    xary = [x_0]
    uary = [u_0]
    yary = [y_0]
    vary = [v_0]

    while t < t_end:
        r3 = np.sqrt(x_0 ** 2 + y_0 ** 2) ** 3

        x_1 = x_0 + u_0 * dt
        u_1 = u_0 - x_0 / r3 * dt

        y_1 = y_0 + v_0 * dt
        v_1 = v_0 - y_0 / r3 * dt

        t += dt

        tary.append(t)
        xary.append(x_1)
        uary.append(u_1)
        yary.append(y_1)
        vary.append(v_1)

        x_0 = x_1
        u_0 = u_1
        y_0 = y_1
        v_0 = v_1

    ax.scatter(xary, yary, label="euler dt=" + str(dt), s=1)
    ax.legend(loc="upper right", fontsize=8)

def rungekutta(t_start, t_end, dt, x_0, u_0, y_0, v_0, ax):

    t = t_start

    tary = [t]
    xary = [x_0]
    uary = [u_0]
    yary = [y_0]
    vary = [v_0]

    while t < t_end:

        r3 = np.sqrt(x_0 ** 2 + y_0 ** 2) ** 3

        x_k1 = u_0
        u_k1 = -x_0 / r3
        y_k1 = v_0
        v_k1 = -y_0 / r3

        r3_k1 = np.sqrt((x_0 + x_k1 * dt/2.0) ** 2 + (y_0 + y_k1 * dt/2.0) ** 2) ** 3

        x_k2 = u_0 + dt / 2.0 * u_k1
        u_k2 = - (x_0 + x_k1 * dt / 2.0) / r3_k1
        y_k2 = v_0 + dt / 2.0 * v_k1
        v_k2 = - (y_0 + y_k1 * dt / 2.0) / r3_k1

        r3_k2 = np.sqrt((x_0 + x_k2 * dt/2.0) ** 2 + (y_0 + y_k2 * dt/2.0) ** 2) ** 3

        x_k3 = u_0 + dt / 2.0 * u_k2
        u_k3 = - (x_0 + x_k2 * dt / 2.0) / r3_k2
        y_k3 = v_0 + dt / 2.0 * v_k2
        v_k3 = - (y_0 + y_k2 * dt / 2.0) / r3_k2

        r3_k3 = np.sqrt((x_0 + x_k3 * dt) ** 2 + (y_0 + y_k3 * dt) ** 2) ** 3

        x_k4 = u_0 + dt * u_k3
        u_k4 = - (x_0 + x_k3 * dt) / r3_k3
        y_k4 = v_0 + dt * v_k3
        v_k4 = - (y_0 + y_k3 * dt) / r3_k3

        x_1 = x_0 + dt / 6.0 * (x_k1 + 2 * x_k2 + 2 * x_k3 + x_k4)
        u_1 = u_0 + dt / 6.0 * (u_k1 + 2 * u_k2 + 2 * u_k3 + u_k4)
        y_1 = y_0 + dt / 6.0 * (y_k1 + 2 * y_k2 + 2 * y_k3 + y_k4)
        v_1 = v_0 + dt / 6.0 * (v_k1 + 2 * v_k2 + 2 * v_k3 + v_k4)

        t += dt

        tary.append(t)
        xary.append(x_1)
        uary.append(u_1)
        yary.append(y_1)
        vary.append(v_1)

        x_0 = x_1
        u_0 = u_1
        y_0 = y_1
        v_0 = v_1

    ax.scatter(xary, yary, label="RungeKutta dt=" + str(dt), s=1)
    ax.legend(loc="upper right", fontsize=8)

# main
if __name__ == "__main__":

    # initial condition
    t = 0.0
    t_end = 10.0
    x_0 = 1.0
    u_0 = 0.0
    y_0 = 0.0
    v_0 = 1.0

    # for plot
    fig = plt.figure(figsize=(8, 4))
    ax1 = fig.add_subplot(121)
    ax2 = fig.add_subplot(122)

    ax1.set_aspect('equal')
    ax2.set_aspect('equal')

    ax1.set_xlim(-2, 2)
    ax1.set_ylim(-2, 2)
    ax2.set_xlim(-2, 2)
    ax2.set_ylim(-2, 2)

    # calculation
    euler(t, t_end, 0.1, x_0, u_0, y_0, v_0, ax1)
    euler(t, t_end, 0.01, x_0, u_0, y_0, v_0, ax1)
    euler(t, t_end, 0.001, x_0, u_0, y_0, v_0, ax1)
    rungekutta(t, t_end * 10, 0.5, x_0, u_0, y_0, v_0, ax2)
    rungekutta(t, t_end * 10, 0.4, x_0, u_0, y_0, v_0, ax2)
    rungekutta(t, t_end * 10, 0.1, x_0, u_0, y_0, v_0, ax2)

    # plot
    plt.show()