import odespy
import numpy as np
import scitools.std as plt

def simulate_drag(
    beta=0.9,                 # dimensionless elasticity parameter
    gamma=0,                  # dimensionless drag parameter
    Theta=30,                 # initial angle in degrees
    epsilon=0,                # initial stretch of wire
    num_periods=6,            # simulate for num_periods
    time_steps_per_period=60, # time step resolution
    plot=True,                # make plots or not
    ):
    from math import sin, cos, pi
    Theta = Theta*np.pi/180  # convert to radians
    # Initial position and velocity
    # (we order the equations such that Euler-Cromer in odespy
    # can be used, i.e., vx, x, vy, y)
    ic = [0,                              # x'=vx
          (1 + epsilon)*sin(Theta),       # x
          0,                              # y'=vy
          1 - (1 + epsilon)*cos(Theta),   # y
          ]

    def f(u, t, beta, gamma):
        vx, x, vy, y = u
        L = np.sqrt(x**2 + (y-1)**2)
        v = np.sqrt(vx**2 + vy**2)
        h1 = beta/(1-beta)*(1 - beta/L)  # help factor
        h2 = gamma/beta*v
        return [-h2*vx - h1*x, vx, -h2*vy - h1*(y-1) - beta, vy]

    # Non-elastic pendulum (scaled similarly in the limit beta=1)
    # solution Theta*cos(t)
    P = 2*pi
    dt = P/time_steps_per_period
    T = num_periods*P
    omega = 2*pi/P

    time_points = np.linspace(
        0, T, num_periods*time_steps_per_period+1)

    solver = odespy.EulerCromer(f, f_args=(beta, gamma))
    solver.set_initial_condition(ic)
    u, t = solver.solve(time_points)
    x = u[:,1]
    y = u[:,3]
    theta = np.arctan(x/(1-y))

    if plot:
        plt.figure()
        plt.plot(x, y, 'b-', title='Pendulum motion',
                 daspect=[1,1,1], daspectmode='equal',
                 axis=[x.min(), x.max(), 1.3*y.min(), 1])
        plt.savefig('tmp_xy.png')
        plt.savefig('tmp_xy.pdf')
        # Plot theta in degrees
        plt.figure()
        plt.plot(t, theta*180/np.pi, 'b-',
                 title='Angular displacement in degrees')
        plt.savefig('tmp_theta.png')
        plt.savefig('tmp_theta.pdf')
        if abs(Theta) < 10*pi/180:
            # Compare theta and theta_e for small angles (<10 degrees)
            theta_e = Theta*np.cos(omega*t)  # non-elastic scaled sol.
            plt.figure()
            plt.plot(t, theta, t, theta_e,
                     legend=['theta elastic', 'theta non-elastic'],
                     title='Elastic vs non-elastic pendulum, '\
                            'beta=%g' % beta)
            plt.savefig('tmp_compare.png')
            plt.savefig('tmp_compare.pdf')
        # Plot y vs x (the real physical motion)
    return x, y, theta, t

def test_equilibrium():
    """Test that starting from rest makes x=y=theta=0."""
    x, y, theta, t = simulate_drag(
        beta=0.9, gamma=0, Theta=0, epsilon=0,
        num_periods=6, time_steps_per_period=10, plot=False)
    tol = 1E-14
    assert np.abs(x.max()) < tol
    assert np.abs(y.max()) < tol
    assert np.abs(theta.max()) < tol

def test_vertical_motion():
    beta = 0.9
    omega = np.sqrt(beta/(1-beta))
    # Find num_periods. Recall that P=2*pi for scaled pendulum
    # oscillations, while here we don't have gravity driven
    # oscillations, but elastic oscillations with frequency omega.
    period = 2*np.pi/omega
    # We want T = N*period
    N = 5
    # simulate function has T = 2*pi*num_periods
    num_periods = 5/omega
    n = 600
    time_steps_per_period = omega*n

    y_exact = lambda t: -0.1*np.cos(omega*t)
    x, y, theta, t = simulate_drag(
        beta=beta, gamma=0, Theta=0, epsilon=0.1,
        num_periods=num_periods,
        time_steps_per_period=time_steps_per_period,
        plot=False)

    tol = 0.00055 # ok tolerance for the above resolution
    # No motion in x direction is epxected
    assert np.abs(x.max()) < tol
    # Check motion in y direction
    y_e = y_exact(t)
    diff = np.abs(y_e - y).max()
    if diff > tol: # plot
        plt.plot(t, y, t, y_e, legend=['y', 'exact'])
        raw_input('Type CR:')
    assert diff < tol, 'diff=%g' % diff

def demo(beta=0.999, gamma=0, Theta=40, num_periods=3):
    x, y, theta, t = simulate_drag(
        beta=beta, gamma=gamma, Theta=Theta, epsilon=0,
        num_periods=num_periods, time_steps_per_period=600,
        plot=True)

if __name__ == '__main__':
    test_equilibrium()
    test_vertical_motion()
    demo(beta=0.999, gamma=1, num_periods=3)
    raw_input('Type CR: ')


"""
This example will walk the user through implementing DCF fits to data sets with
turning points and inflection points. It builds on the details in the
'Simple Example Code' and uses the 'constraints' keyword argument introduced
there. The 'constraints' keyword argument is used to adjust the type of DCF that
is being fitted. Recall that by default ``maxsmooth`` implements a Maximally
Smooth Function or MSF with constraints=2 i.e. derivatives of order :math:`{m \geq 2}`
constrained so that they do not cross zero. This allows for turning points in the
DCF as illustrated below.

We start by generating some noisy data that we know will include a turning point
and defining the order of the DCF we would like to fit.

.. code:: bash

    import numpy as np

    x = np.linspace(-10, 10, 100)
    noise = np.random.normal(0, 0.02, 100)
    y = x**(2) + noise

    N = 10

"""
import numpy as np

x = np.linspace(-10, 10, 100)
noise = np.random.normal(0, 0.02, 100)
y = x**(2) + noise

N = 10

"""

We can go ahead and plot this data just to double check it features a turning
point.

.. code:: bash

    import matplotlib.pyplot as plt

    plt.plot(x, y)
    plt.xlabel('x', fontsize=12)
    plt.ylabel('y', fontsize=12)
    plt.show()

"""

import matplotlib.pyplot as plt

plt.plot(x, y)
plt.xlabel('x', fontsize=12)
plt.ylabel('y', fontsize=12)
plt.show()

"""
As already stated ``maxsmooth`` does not constrain the first derivative of the
DCF by default so we can go ahead and fit the data.

.. code:: bash

    from maxsmooth.DCF import smooth

    res = smooth(x, y, N)

"""

from maxsmooth.DCF import smooth

res = smooth(x, y, N)

"""
If we than plot the resultant residuals we will see that despite the data
having a turning point present we have recovered the Gaussian noise.

.. code:: bash

    plt.plot(x, y- res.y_fit, label='Recovered Noise')
    plt.plot(x, noise, label='Actual Noise')
    plt.ylabel(r'$\delta y$', fontsize=12)
    plt.xlabel('x', fontsize=12)
    plt.legend()
    plt.show()

"""

plt.plot(x, y- res.y_fit, label='Recovered Noise')
plt.plot(x, noise, label='Actual Noise')
plt.ylabel(r'$\delta y$', fontsize=12)
plt.xlabel('x', fontsize=12)
plt.legend()
plt.show()

"""
To illustrate what happens when there is an inflection point in the data we can
define some sinusoidal data as so.

.. code:: bash

    x = np.linspace(1, 5, 100)
    noise = np.random.normal(0, 0.02, 100)
    y = np.sin(x) + noise

    N = 10

    plt.plot(x, y)
    plt.xlabel('x', fontsize=12)
    plt.ylabel('y', fontsize=12)
    plt.show()

"""
x = np.linspace(1, 5, 100)
noise = np.random.normal(0, 0.02, 100)
y = np.sin(x) + noise

N = 10

plt.plot(x, y)
plt.xlabel('x', fontsize=12)
plt.ylabel('y', fontsize=12)
plt.show()

"""
If we proceed to fit this with smooth() in its default settings we will get a
poor fit as by default the second derivative is constrained. We need to lift this
constraint to allow for the prominent inflection point to be modelled. We do this
by setting the keyword argument constraints=3 creating a Partially Smooth Function
or PSF.

.. code:: bash

    res_msf = smooth(x, y, N)
    res_psf = smooth(x, y, N, constraints=3)

    plt.plot(x, y, label='Data')
    plt.plot(x, res_msf.y_fit, label=r'MSF fit, $m \geq 2$')
    plt.plot(x, res_psf.y_fit, label=r'PSF fit, $m \geq 3$')
    plt.xlabel('x', fontsize=12)
    plt.ylabel('y', fontsize=12)
    plt.legend()
    plt.show()

"""

res_msf = smooth(x, y, N)
res_psf = smooth(x, y, N, constraints=3)

plt.plot(x, y, label='Data')
plt.plot(x, res_msf.y_fit, label=r'MSF fit, $m \geq 2$')
plt.plot(x, res_psf.y_fit, label=r'PSF fit, $m \geq 3$')
plt.xlabel('x', fontsize=12)
plt.ylabel('y', fontsize=12)
plt.legend()
plt.show()

"""
Finally, we can plot the residuals to further see that by lifting the constraint on the
second derivative we have allowed an inflection point in the data.

.. code::

    plt.plot(x, y- res_psf.y_fit, label='Recovered Noise')
    plt.plot(x, noise, label='Actual Noise')
    plt.ylabel(r'$\delta y$', fontsize=12)
    plt.xlabel('x', fontsize=12)
    plt.legend()
    plt.show()

"""

plt.plot(x, y - res_psf.y_fit, label='Recovered Noise')
plt.plot(x, noise, label='Actual Noise')
plt.ylabel(r'$\delta y$', fontsize=12)
plt.xlabel('x', fontsize=12)
plt.legend()
plt.show()


'''Testing finite difference coefficients
    d      : d
    p      : puracy
    method : forward (ffd), backward (bfd), center (cfd)
'''

import pytest
import numpy as np
import scipy.sparse as sps
from ..diffoper import findifcoef

def findifsten(d, p, method):
    "Finite difference stencil."

    if method == 'ffd':
        sten = np.arange(d + p, dtype=np.int32)
    elif method == 'bfd':
        sten = np.arange(0, -(d + p), -1, dtype=np.int32)
    elif method == 'cfd':
        smax = (d + p - 1)//2
        sten = np.arange(-smax, smax+1, dtype=np.int32)

    return sten

def tabulatedcoef(d, p, method):
    "Return tabulated finite difference coefs."

    if method == 'ffd' or method == 'bfd':
        if d == 1 and p == 2:
            coef = np.array([-3/2, 2, -1/2])
        if d == 2 and p == 6:
            coef = np.array([469/90, -223/10, 879/20, -949/18, 41, -201/10, 1019/180, -7/10])
        if d == 4 and p == 4:
            coef = np.array([28/3, -111/2, 142, -1219/6, 176, -185/2, 82/3, -7/2])

    if method == 'cfd':
        if d == 1 and p == 2:
            coef = np.array([-1/2, 0, 1/2])
        if d == 2 and p == 6:
            coef = np.array([1/90, -3/20, 3/2, -49/18, 3/2, -3/20, 1/90])
        if d == 4 and p == 4:
            coef = np.array([-1/6, 2, -13/2, 28/3, -13/2, 2, -1/6])

    # For odd derivatives bfd = -ffd
    if method == 'bfd' and d % 2 != 0:
        coef = -1 * coef

    return coef

@pytest.mark.parametrize('d, p', [(1, 2), (2, 6), (4, 4)])
def test_findifcoef(d, p):

    for method in ['ffd', 'bfd', 'cfd']:
        sten = findifsten(d, p, method)          # stencil
        coef = findifcoef(sten, d)               # calculated
        refr = tabulatedcoef(d, p, method)       # expected
        assert np.allclose(coef, refr)


import numpy as np

# problem 1
class Precise(object):
    def __init__(self, val=0, exp=0):
        self.val = val
        # note, I'm storing the power of the last significant digit.
        self.sig = exp - len(str(self.val)) +1

    def exp(self):
        # get the exponent from the sig value.
        return len(str(self.val)) - 1 + self.sig

    def pyfloat(self):
        # return a python float from the floating point object.
        return self.val * 10**(self.sig)

    def copy(self):
        # make a copy of the floating point object.
        new = precise(val=self.val)
        new.sig = self.sig
        return new

    def __repr__(self):
        # print method
        s = str(self.val)
        return s[0] + '.' + s[1:] + " x 10^" + str(self.exp())

    def __add__(self, other):
        # addition
        if self.sig < other.sig:
            new = self.copy()
            new.val *= 10**(other.sig - self.sig)
            new.sig = self.sig
            new.val += other.val
        elif other.sig < self.sig:
            new = other.copy()
            new.val *= 10**(self.sig - other.sig)
            new.sig = other.sig
            new.val += self.val
        else:
            new = other.copy()
            new.val += self.val
        return new

    def __mul__(self, other):
        # multiplication
        new = self.copy()
        new.sig += other.sig
        new.val *= other.val
        return new

    def __sub__(self, other):
        # subtraction
        new = other.copy()
        new.val *= -1
        return self + new

    def truncate(self, newsig):
        # truncation
        if self.sig < newsig:
            self.val = int(str(self.val)[:(self.sig-newsig)])
            self.sig = newsig

class Significant(object):
    # most of these methods are based on the previous class
    def __init__(self, val=Precise(), err=Precise()):
        self.val = val
        # note: error should ALWAYS be positive
        self.err = err

    def pyfloat(self):
        return self.val.pyfloat()

    def copy(self):
        new = significant()
        new.val = self.val.copy()
        new.err = self.err.copy()
        return new

    def __repr__(self):
        return str(self.val) + " +- " + str(self.err)

    def __add__(self, other):
        new = self.copy()
        new.val += other.val
        new.err += other.err
        return new

    def __sub__(self, other):
        new = self.copy()
        new.val -= other.val
        new.err += other.err
        return new

    def __mul__(self, other):
        new = self.copy()
        new.val *= other.val
        new.err  = self.err*other.val + self.val*other.err + self.err*other.err
        return new

    def truncate(self):
        if self.err.val == 1:
            self.err.sig = self.err.exp()
            self.val.truncate(self.err.sig)
        elif self.err.val != 0:
            self.err.sig = self.err.exp() + 1
            self.err.val = 1
            self.val.truncate(self.err.sig)

# problem 2
def sqrt64(A, reps):
    Ac = A.copy()
    I = Ac.view(dtype=np.int64)
    I >>= 1
    I += (1<<61) - (1<<51)
    for i in xrange(reps):
        Ac = .5 *(Ac + A / Ac)
    return Ac

# For the analysis, it is system dependent, but the sqrt here will probably be faster for low accuracy.
# Again, for the fast inverse square root, it is system dependent, but the cython version here
# should still be moderately faster than the version using the sqrt function in C for up to 4 iterations
# which is just as accurate.


# K-Means Clustering
# This chapter introduces a different clustering algorithm - k-means clustering - and its implementation in SciPy. K-means clustering overcomes the biggest drawback of hierarchical clustering that was discussed in the last chapter. As dendrograms are specific to hierarchical clustering, this chapter discusses one method to find the number of clusters before running k-means clustering. The chapter concludes with a discussion on the limitations of k-means clustering and discusses considerations while using this algorithm.

# K-means clustering: first exercise
# This exercise will familiarize you with the usage of k-means clustering on a dataset. Let us use the Comic Con dataset and check how k-means clustering works on it.

# Recall the two steps of k-means clustering:

# Define cluster centers through kmeans() function. It has two required arguments: observations and number of clusters.
# Assign cluster labels through the vq() function. It has two required arguments: observations and cluster centers.
# The data is stored in a Pandas data frame, comic_con. x_scaled and y_scaled are the column names of the standardized X and Y coordinates of people at a given point in time.

# Import the kmeans and vq functions
from scipy.cluster.vq import kmeans, vq

# Generate cluster centers
cluster_centers, distortion = kmeans(comic_con[['x_scaled','y_scaled']],2)

# Assign cluster labels
comic_con['cluster_labels'], distortion_list = vq(comic_con[['x_scaled', 'y_scaled']], cluster_centers)

# Plot clusters
sns.scatterplot(x='x_scaled', y='y_scaled',
                hue='cluster_labels', data = comic_con)
plt.show()

# Elbow method on distinct clusters
# Let us use the comic con data set to see how the elbow plot looks on a data set with distinct, well-defined clusters. You may want to display the data points before proceeding with the exercise.

# The data is stored in a Pandas data frame, comic_con. x_scaled and y_scaled are the column names of the standardized X and Y coordinates of people at a given point in time.

distortions = []
num_clusters = range(1, 7)

# Create a list of distortions from the kmeans function
for i in num_clusters:
    cluster_centers, distortion = kmeans(comic_con[['x_scaled', 'y_scaled']], i)
    distortions.append(distortion)

# Create a data frame with two lists - num_clusters, distortions
elbow_plot = pd.DataFrame({'num_clusters': num_clusters, 'distortions': distortions})

# Creat a line plot of num_clusters and distortions
sns.lineplot(x='num_clusters', y='distortions', data = elbow_plot)
plt.xticks(num_clusters)
plt.show()

# Elbow method on uniform data
# In the earlier exercise, you constructed an elbow plot on data with well-defined clusters. Let us now see how the elbow plot looks on a data set with uniformly distributed points. You may want to display the data points on the console before proceeding with the exercise.

# The data is stored in a Pandas data frame, uniform_data. x_scaled and y_scaled are the column names of the standardized X and Y coordinates of points.

distortions = []
num_clusters = range(2, 7)

# Create a list of distortions from the kmeans function
for i in num_clusters:
    cluster_centers, distortion = kmeans(uniform_data[['x_scaled','y_scaled']],i)
    distortions.append(distortion)

# Create a data frame with two lists - number of clusters and distortions
elbow_plot = pd.DataFrame({'num_clusters': num_clusters, 'distortions': distortions})

# Creat a line plot of num_clusters and distortions
sns.lineplot(x='num_clusters', y='distortions', data=elbow_plot)
plt.xticks(num_clusters)
plt.show()

# Impact of seeds on distinct clusters
# You noticed the impact of seeds on a dataset that did not have well-defined groups of clusters. In this exercise, you will explore whether seeds impact the clusters in the Comic Con data, where the clusters are well-defined.

# The data is stored in a Pandas data frame, comic_con. x_scaled and y_scaled are the column names of the standardized X and Y coordinates of people at a given point in time.

# Import random class
from numpy import random

# Initialize seed
random.seed(0)

# Run kmeans clustering
cluster_centers, distortion = kmeans(comic_con[['x_scaled', 'y_scaled']], 2)
comic_con['cluster_labels'], distortion_list = vq(comic_con[['x_scaled', 'y_scaled']], cluster_centers)

# Plot the scatterplot
sns.scatterplot(x='x_scaled', y='y_scaled',
                hue='cluster_labels', data = comic_con)
plt.show()

# Uniform clustering patterns
# Now that you are familiar with the impact of seeds, let us look at the bias in k-means clustering towards the formation of uniform clusters.

# Let us use a mouse-like dataset for our next exercise. A mouse-like dataset is a group of points that resemble the head of a mouse: it has three clusters of points arranged in circles, one each for the face and two ears of a mouse.

# Here is how a typical mouse-like dataset looks like (Source).

# The data is stored in a Pandas data frame, mouse. x_scaled and y_scaled are the column names of the standardized X and Y coordinates of the data points.

# Import the kmeans and vq functions
from scipy.cluster.vq import kmeans,vq

# Generate cluster centers
cluster_centers, distortion = kmeans(mouse[['x_scaled','y_scaled']],3)

# Assign cluster labels
mouse['cluster_labels'], distortion_list = vq(mouse[['x_scaled','y_scaled']],cluster_centers)

# Plot clusters
sns.scatterplot(x='x_scaled', y='y_scaled',
                hue='cluster_labels', data = mouse)
plt.show()

# FIFA 18: defenders revisited
# In the FIFA 18 dataset, various attributes of players are present. Two such attributes are:

# defending: a number which signifies the defending attributes of a player
# physical: a number which signifies the physical attributes of a player
# These are typically defense-minded players. In this exercise, you will perform clustering based on these attributes in the data.

# The following modules have been pre-loaded: kmeans, vq from scipy.cluster.vq, matplotlib.pyplot as plt, seaborn as sns. The data for this exercise is stored in a Pandas dataframe, fifa. The scaled variables are scaled_def and scaled_phy

# Set up a random seed in numpy
random.seed([1000,2000])

Cluster Analysis in Python/K-Means_Clustering.py


#!/usr/bin/env python

"""
Author  : Bhekimpilo Ndhlela
Author  : 18998712
module  : TW244 Applied Mathematics
task    : src code for Assignment04
"""
from scipy.special import lambertw
from scipy.optimize import fsolve
from scipy.linalg import eig, expm
import matplotlib.pyplot as plt
import numpy as np

def plot_question3(x, y, point=None, point_plot=False):
    plt.title("IVP solution Plot:")
    plt.plot(x, y[:,0], 'k-', lw=4, label='x1')
    plt.plot(x, y[:,1], 'r-', lw=4, label='x2')
    plt.ylabel('y')
    if point_plot != False:
        plt.plot(10, point[0], 'ro', lw=4, label='x1(10)')
        plt.plot(10, point[1], 'ko', lw=4, label='x2(10)')
        plt.xlabel('0 <= t <= 10')
        plt.xlim(0, 10)
    else:
        plt.xlabel('0 <= t <= 2')
        plt.xlim(0, 2)
    plt.legend(loc='best')
    plt.grid(True)
    plt.show()

def f(R):
    sind = lambda theta_radians: np.degrees(np.sin(theta_radians))
    cosd = lambda theta_radians: np.degrees(np.cos(theta_radians))
    g, r, theta, v0 = 9.81, 0.14, 45.0, 44.7 # constants
    print (g/r + v0*sind(theta))*(R/(v0*cosd(theta))) + (g/r**2)*np.log(1-(r*R)/(v0*cosd(theta)))
    return (g/r + v0*sind(theta))*(R/(v0*cosd(theta))) + (g/r**2)*np.log(1-(r*R)/(v0*cosd(theta)))

if __name__ == '__main__':
    # question 1
    #a.)
    z = fsolve(lambda z: z*np.exp(z) - 1.0, 0.0)
    #print z
    #b.)    i.
    w = lambertw(1).real
    #print w
    #b.)    ii.
    wn = w *np.exp(w)
    #print wn

    # question 2
    print '{:.17f}'.format(1.7356e-14 )
    R = fsolve(f, )
    print 'R = ', R

    # question 3
    x0 = np.array([2, -0.5])
    A, time = np.array([[-0.50, 3.0], [0.75, -2.0]]), np.linspace(0, 2, 1000)
    eigvals, eigvects = eig(A) #eigenvaluse and eigen vectors of: A
    eigvals = eigvals.real #take the real part of the eigvalues
    c = np.linalg.solve(eigvects, x0)   #z = inverse(eigenvectors)b

    x1 = lambda t: c[0]*np.exp(eigvals[0]*t)*eigvects[0][0] + c[1]*np.exp(eigvals[1]*t)*eigvects[0][1]
    x2 = lambda t: c[0]*np.exp(eigvals[0]*t)*eigvects[1][0] + c[1]*np.exp(eigvals[1]*t)*eigvects[1][1]

    x = np.array([(x1(t), x2(t)) for t in time])
    #plot_question3(time, x, point=None)

    eA = np.dot(expm(10 *A), x0) # get the matrix exponintiation and multiply by x(0)
    time = np.linspace(0, 10, 1000)
    x = np.array([(x1(t), x2(t)) for t in time])
    #plot_question3(time, x, point=eA, point_plot=True)

    #question 4

else:
    import sys
    sys.exit('USAGE: python assignment04.py')


# coding: utf-8

# # Function dctmatrix
#
# ## Synopse
#
# Compute the Kernel matrix for the DCT Transform.
#
# - **A = dctmatrix(N)**
#
#   - **A**:output: DCT matrix NxN.
#   - **N**:input: matrix size (NxN).

# In[1]:

import numpy as np

def dctmatrix(N):
    x = np.resize(range(N), (len(range(N)), len(range(N)))) #matrix with columns index
    u = np.transpose(np.resize(range(N), (len(range(N)), len(range(N))))) #matrix with rows index
    alpha = np.ones((N,N)) * np.sqrt(2./N) # 1/sqrt(2/N)
    alpha[0,:] = np.sqrt(1./N) # alpha(u,x)
    A = alpha * np.cos((2*x+1)*u*np.pi / (2.*N)) # Cn(u,x)
    return A

# ## Examples

# In[1]:

testing = (__name__ == "__main__")
if testing:
    get_ipython().system(' jupyter nbconvert --to python dctmatrix.ipynb')
    import numpy as np
    import sys,os
    ea979path = os.path.abspath('../../')
    if ea979path not in sys.path:
        sys.path.append(ea979path)
    import ea979.src as ia

# ### Example 1

# In[3]:

if testing:
    np.set_printoptions(suppress=True, precision=4)
    A = ia.dctmatrix(4)
    print('Visualiza matriz DCT 4x4:\n',A)
    B = np.dot(A,np.transpose(A))

    print("\nVisualiza propriedade A*A'= I:\n", B)

# ### Example 2

# In[2]:

if testing:
    get_ipython().magic('matplotlib inline')
    import matplotlib.pyplot as plt
    import matplotlib.image as mpimg
    import sys,os
    ea979path = os.path.abspath('../../')
    if ea979path not in sys.path:
        sys.path.append(ea979path)
    import ea979.src as ia
    A = ia.dctmatrix(128)
    ia.adshow(ia.normalize(A,[0,255]),'DCT 128x128')

# In[ ]:

# In[ ]:


import numpy as np
from myutil.probability_statistics_util import cal_R2

def calibrate_alpha_beta(t,X):
    '''
    Use Least Square method to applying "point estimate" on alpha and beta,
    where X[k]=alpha*L*t[k]+beta
    '''
    cal_t_count=t.shape[0]
    _,H,W=X.shape
    t=t.reshape(-1,1)
    A=np.concatenate([t,np.ones((cal_t_count,1))],1)
    ls_estimation=np.linalg.inv(A.T@A)@A.T@X.reshape(cal_t_count,-1)
    ls_estimation=ls_estimation.reshape(2,H,W)
    alpha_L=ls_estimation[0]
    beta=ls_estimation[1]
    # calculate the Coefficient_of_determination
    X_hat=alpha_L.reshape(1,H,W)*t.reshape(cal_t_count,1,1)+beta.reshape(1,H,W)
    R2=cal_R2(X,X_hat)
    alpha=alpha_L
    return alpha,beta,R2


import math
import numpy as np

def to_binary(x_i):
    #this can probably also be done with round()
    return 1 if x_i > 0.5 else 0

def hypothesis(theta, x_i):
    z = np.dot(theta, x_i)
    sigmoid = 1.0 / (1.0 + math.exp(-1.0*z))
    return 0.9999999999999 if sigmoid == 1 else sigmoid

def determine_correct_guesses(X, Y, theta, m):
    determined_Y = [np.dot(theta, X[ii]) for ii in range(m)]
    determined_Y_binary = [to_binary(elem) for elem in determined_Y]
    correct = 0
    for ii in range(0,m):
        if determined_Y_binary[ii] == Y[ii]:
            correct+=1
    return correct

def gradient_descent(X, Y, theta, alpha, m, number_of_iterations=1000):
    for iter in range(0,number_of_iterations):
        cost = (-1.0/m)*sum([Y[ii]*math.log(hypothesis(theta, X[ii]))+(1-Y[ii])*math.log(1-hypothesis(theta, X[ii])) for ii in range(m)])
        grad = (-1.0/m)*sum([X[ii]*(Y[ii]-hypothesis(theta, X[ii])) for ii in range(m)])
        theta = theta - alpha * grad
        correct = determine_correct_guesses(X, Y, theta, m)
        print "iteration %s : cost %s : correct_guesses %s / %s" % (iter, cost, correct, len(Y))
    return theta


import scipy.optimize
import numpy as np
import sys

def pay_monthly_on_loan(initial_value, apr, duration_years, monthly_payment):
    principal = {0:initial_value}
    for year in np.arange(1,duration_years):
        principal[year] = principal[year-1] * apr - 12 * monthly_payment
    return principal[year]

def find_monthly_payment(loan_value, apr, duration_years):
    principal_remaining = lambda monthly_payment : pay_monthly_on_loan(loan_value, apr, duration_years, monthly_payment)
    # Use a Brent gradient approach method of solving roots to determine the proper value
    payment_needed = scipy.optimize.brentq(principal_remaining, 0, loan_value * (apr**duration_years))
    return round(payment_needed)

def pay_yearly_on_loan(initial_value, apr, duration_years, yearly_payment):
    principal = {0:initial_value}
    for year in np.arange(1,duration_years):
        principal[year] = principal[year-1] * apr - yearly_payment
    return principal[year]

def find_yearly_payment(loan_value, apr, duration_years):
    principal_remaining = lambda yearly_payment : pay_yearly_on_loan(loan_value, apr, duration_years, yearly_payment)
    # Use a Brent gradient approach method of solving roots to determine the proper value
    payment_needed = scipy.optimize.brentq(principal_remaining, 0, loan_value * (apr**duration_years))
    return round(payment_needed)

if __name__ is '__main__':
	loan_value = 400 * 1000
	apr = 1.05
	monthly_30 = find_monthly_payment(loan_value, apr, 30)
	monthly_15 = find_monthly_payment(loan_value, apr, 15)
	paid_30 = monthly_30 * 12 * 30
	paid_15 = monthly_15 * 12 * 15
	print(f'For {loan_value}@{apr}, the 30-year payment is {monthly_30} while it\'s {monthly_15} for 15, totalling {paid_30} and {paid_15} respectively')


# --------------
#Importing header files
import pandas as pd
import scipy.stats as stats
import math
import numpy as np
import matplotlib.pyplot as plt
from statsmodels.stats.weightstats import ztest
from statsmodels.stats.weightstats import ztest
from scipy.stats import chi2_contingency

import warnings

warnings.filterwarnings('ignore')
#Sample_Size
sample_size=2000

#Z_Critical Score
z_critical = stats.norm.ppf(q = 0.95)

# Critical Value
critical_value = stats.chi2.ppf(q = 0.95, # Find the critical value for 95% confidence*
                      df = 6)   # Df = number of variable categories(in purpose) - 1

#Reading file
data=pd.read_csv(path)

#Code starts here
#create sample of data
data_sample = data.sample(n = sample_size , random_state = 0)

sample_mean = data_sample['installment'].mean()
population_std = data['installment'].std()
margin_of_error = z_critical * (population_std/math.sqrt(sample_size))
confidence_interval = (sample_mean - margin_of_error,
                       sample_mean + margin_of_error)
true_mean = data['installment'].mean()
print(confidence_interval)
print(true_mean)

sample_size = np.array([20, 50, 100])

fig, axes = plt.subplots(3,1, figsize = (10,20))

#running loop to iterate over rows
for i in range(len(sample_size)):
    m = []
    #loop to implement number of samples
    for j in range(1000):
        mean = data['installment'].sample(sample_size[i]).mean()
        #mean = j +1
        m.append(mean)
    #convert list to series
    mean_series = pd.Series(m)
    axes[i].hist(mean_series, normed = True)
#plt.show()

#Small busiess interests
data["int.rate"] = data["int.rate"].map(lambda x: str(x)[:-1])
print(data["int.rate"].head())
data["int.rate"] = data["int.rate"].astype(float)/100
z_statistic_1, p_value_1 = ztest(data[data['purpose'] == 'small_business']['int.rate'], value=data['int.rate'].mean(), alternative = 'larger')

print(('z-statistic_1 is :{}'.format(z_statistic_1)))
print(('p_value_1 is :{}'.format(p_value_1)))

#Loan defaulting
z_statistic_2, p_value_2 = ztest(x1 = data[data['paid.back.loan'] == 'No']['installment'], x2= data[data['paid.back.loan'] == 'Yes']['installment'] )
print(('z-statistic_2 is :{}'.format(z_statistic_2)))
print(('p_value_2 is :{}'.format(p_value_2)))

#Chi2 test
critical_value = stats.chi2.ppf(q=0.95, df = 6)
yes = data[data['paid.back.loan'] == 'Yes']['purpose'].value_counts()
no = data[data['paid.back.loan'] == 'No']['purpose'].value_counts()

observed = pd.concat([yes.transpose(), no.transpose()], 1, keys = ['Yes', 'No'])
print(observed)

chi2, p, dof, ex = chi2_contingency(observed)

print (critical_value, chi2)

import numpy,import scipy,from scipy,from statsmodels


import scipy
import scipy.stats as ss
import numpy as np
import matplotlib
import pandas as pd
import random
import math

def iqr_threshold_method(scores, margin):
    q1 = np.percentile(scores, 25, interpolation='midpoint')
    q3 = np.percentile(scores, 75, interpolation='midpoint')
    iqr = q3-q1
    lower_range = q1 - (1.5 * iqr)
    upper_range = q3 + (1.5 * iqr)
    lower_range = lower_range - margin
    upper_range = upper_range + margin
    return lower_range, upper_range

def angle(point1, point2, point3):
    v21 = np.subtract(point2, point1)
    v31 = np.subtract(point3, point1)
    dot_product = (v21*v31).sum()
    normalization = np.linalg.norm(v21)*np.linalg.norm(v31)
    acute_angle = np.arccos(dot_product/normalization)
    return acute_angle

def eval_angle_point(point, data):
    angles_data = []
    for index_b, b in enumerate(data):
        if (np.array_equal(b, point)):
            continue

        for c in data[index_b + 1:]:
            if (np.array_equal(c, point)) or (np.array_equal(c, b)):
                continue
            angles_data.append(angle(point, b, c))
    return angles_data

def AngleOutlier(data, margin=0):
    """Returns numpy array with data points labelled as outliers
    Parameters
    ----------
    data: numpy 2d array like data points

    margin: int, default=0
        Margin of error
    """
    no_of_data_point = data.shape[0]
    variance_of_each_datapoint = []

    for i in range(0, no_of_data_point):
        point = data[i]
        temp = eval_angle_point(point, data)
        variance_of_each_datapoint.append(np.var(temp))

    lower_range, upper_range = iqr_threshold_method(variance_of_each_datapoint, margin)

    outlier_points = []

    for i in range(0, no_of_data_point):
        if variance_of_each_datapoint[i] < lower_range or variance_of_each_datapoint[i] > upper_range:
            outlier_points.append(data[i])

    return outlier_points, lower_range, upper_range, variance_of_each_datapoint

if __name__=='__main__':
    np.random.seed(16)

    normal_mean = np.array([1.0, 2.0])
    normal_covariance = np.array([[0.2, 0.0], [0.0, 0.1]])
    normal_data = np.random.multivariate_normal(normal_mean, normal_covariance, 10)

    anomaly_mean = np.array([6.0, 8.0])
    anomaly_covariance = np.array([[2.0, 0.0], [0.0, 4.0]])
    anomaly_data = np.random.multivariate_normal(anomaly_mean, anomaly_covariance, 10)
    all_data = np.concatenate((normal_data, anomaly_data), axis=0)
    print(all_data)
    print(all_data.shape)
#    point = all_data[0]
    #print(point)
    #res = eval_angle_point(point, all_data)
    res = AngleOutlier(all_data)
    print(res)
    #print(res)


import random
import matplotlib.pyplot as plt
import numpy as np
import math
from typing import List

def modelo(x):
    return 2 * math.exp(0.3 * x) + 3 * random.random()

# a, b = 0, 10
# x = a + (b - a) * np.random.rand(50)
# x.sort()
# y = [modelo(xi) for xi in x]

x = [0.0251, 0.6401, 0.8416, 1.1842, 1.72, 2.0679, 2.3642, 2.854]
y = [2.2251, 4.7429, 5.4953, 6.3624, 11.3061, 15.0322, 19.9302, 31.3503]

def best_exp(x: List[float], y: List[float]):
    sum_x = sum(x)
    sum_x2 = sum(xi ** 2 for xi in x)
    A = [[len(x), sum(x)], [sum_x, sum_x2]]
    y_ = [math.log(yi) for yi in y]
    sum_xy = sum(xi * yi for xi, yi in zip(x, y_))
    B = [sum(y_), sum_xy]
    a0, a1 = np.linalg.solve(A, B)
    a, b = math.exp(a0), a1
    return a, b

a, b = best_exp(x, y)

def bexp(x):
    return a * math.exp(b * x)

if __name__ == '__main__':

    print(a, b)
    t = np.linspace(min(x), max(x), 100)
    bexpt = [bexp(i) for i in t]
    plt.plot(t, bexpt)
    plt.scatter(x, y)
    plt.savefig('pontos_exp.png')


import sympy
'''
@alt(シンボル|記号|変数)
@prefix(z;[変数|パラメータ])
@prefix(e;[数式|数値])

[代数計算|シンボル計算|数式処理][|モジュール|ライブラリ]を使う
'''

s = 'z'

sympy.Symbol(s)
'''
sをシンボルに変換する
sを代数計算の変数に変換する
sの変数名を数式に変換する
'''

z = sympy.Symbol(s)
'''
sを[シンボル|変数][に|化]して、zにする
'''

e = e2 = sympy.Symbol(s)
n = 2
oo = sympy.oo

e.subs(z, n)
'''
eのzにnを代入する
'''

e.subs(z, e2)
'''
eのzにe2を代入する
eのzをe2で置き換える
'''

sympy.expand(e)
'''
eを展開する
eの展開を行う
'''

sympy.factor(e)
'''
eを因数分解する
eの因数分解を行う
'''

sympy.sympify(e)
'''
eを簡単[に|化]する
eを簡略[に|化]する
eの簡[略|単]化を行う
'''

sympy.apart(e)
'''
eを部分分数[に|として]展開する
eの部分分数化[を行う|する]
'''
# Expand `e` into partial fractions

sympy.solve(e)
'''
{eを|方程式として}解く
方程式の解[|を求める]
'''

# Solve a formula as an equation
# Solve a equation `e`
# Find solutions to equations

sympy.solve(e, z)
'''
{方程式として|eのzの解を}求める
'''

sympy.solve([e, e2])
'''
連立方程式の解を求める
連立方程式を解く
'''

# Solve simultaneous equations
# Find solutions to simultaneous equations

sympy.limit(e, z, 0)
'''
@alt(とき|時|場合|際)

zが0に近づくとき[の|、]eの極限値を求める
'''

sympy.limit(e, z, oo)
'''
zが無限大に近づくとき[の|、]eの極限値を求める
'''

sympy.limit(e, z, -oo)
'''
zがマイナス無限大に近づくとき[の|、]eの極限値を求める
'''

sympy.diff(e)
'''
eを微分する
eの微分を求める
'''

sympy.diff(e, z)
'''
zについてeの微分を[行う|求める]
{eを|zについて}微分する
'''

sympy.diff(e, z, n)
'''
{eを|zについて}n階微分する
eの[zについての|]n階微分を[求める|行う]
'''

sympy.integrate(e)
'''
eを積分する
eの[積分|インテグラル]を[求める|行う]
'''

sympy.integrate(e, z)
'''
zについてeを積分する
zについてeの[積分|インテグラル]を[求める|行う]
'''

e = sympy.E
float(e)
'''
eの数値[を求める|]
eを数値計算する
eを[数値|浮動小数点数]に変換する
'''

sympy.sqrt(e)
'''
eの平方根を求める
'''

# sympy.E**(sympy.I * sympy.pi) == -1
# '''
# オイラーの等式を使う
# '''

# sympy.summation(e, (z, 1, N))
# '''
# @test(import sympy;z,N=sympy.Symbol('z N');e=z**2;$$)
# eの総和[|を求める]
# '''


import numpy as np
import pandas as pd

class OLS_Simu:
    """Generate an OLS object with randomized data

    Parameters
    ----------
    n, k, beta: int, int, list
        n is the sample size, k is the number of parameters including constant,
        beta is a list of predetermined OLS parameters.
    """
    def __init__(self, n, k, beta):

        self.n = n
        self.k = k

        # generate X matrix
        self.const = np.ones(self.n)
        self.const = self.const[np.newaxis, :]
        self.X_inde = np.random.randn(self.k-1, self.n)
        self.X = np.concatenate((self.const.T, self.X_inde.T), axis=1)

        # set beta a vertical vector
        self.beta = beta
        if len(self.beta) == self.k:
            self.beta_array = np.array(self.beta)
            self.beta_array = self.beta_array[np.newaxis, :].T
        else:
            raise Exception('length of beta is not equal to k!')

        # generate set disturbance term as a vertical vector
        self.u = np.random.randn(self.n)
        self.u = self.u[np.newaxis, :].T

        # generate y
        self.y = self.X@self.beta_array + self.u

        # estimates and residuals
        self.beta_hat = np.linalg.inv(self.X.T@self.X)@self.X.T@self.y
        self.resid =  self.y - self.X@self.beta_hat

#################################################
    def ols_estimates(self):
        """Estimate the model and return the beta_hat and residuals."""
        self.beta_hat = np.linalg.inv(self.X.T@self.X)@self.X.T@self.y
        self.resid =  self.y - self.X@self.beta_hat
        return self.beta_hat, self.resid

#################################################
    def anova(self):
        """ANOVA decomposition"""
        self.TSS = np.linalg.norm(self.y)
        self.ESS = np.linalg.norm(self.X@self.beta_hat)
        self.RSS = np.linalg.norm(self.y)-np.linalg.norm(self.X@self.beta_hat)
        return self.TSS, self.ESS, self.RSS

#################################################
    def proj_mat_P(self):
        """Projection matrix P"""
        self.proj_matP = self.X@np.linalg.inv(self.X.T@self.X)@self.X.T
        return self.proj_matP
#################################################
    def proj_mat_M(self):
        """Projection matrix M"""
        self.proj_matM =  np.eye(self.X.shape[0]) - self.X@np.linalg.inv(self.X.T@self.X)@self.X.T
        return self.proj_matM
#################################################
    def s_sqr(self):
        """Unbiased estimator of sigma square."""
        return np.sum(self.resid**2)/(len(self.resid)-self.k)
#################################################
    def cov_beta_hat(self):
        """Covariance matrix of estimated coefficients beta, standar"""
        self.cov_beta_hat = np.sum(self.resid**2)/(len(self.resid)-self.k)*np.linalg.inv(self.X.T@self.X)
        self.cov_beta_hat_prin_diag = self.cov_beta_hat.diagonal()
        return (self.cov_beta_hat, self.cov_beta_hat_prin_diag)

##################################################
##################################################
##################################################
##################################################
def gen_X(n, k):
    """
    Parameters
    ----------
    n, k: int
        generate a random explanatory matrxi with constant term of size n by k, n is the sample size,
        k is the number of parameters.
    Returns
    ----------
    X: ndarray
        matrix styled n by k random array generated by np.random.randn().
    """
    const = np.ones(n)
    const = const[np.newaxis, :]
    X_inde = np.random.randn(k-1, n)
    X = np.concatenate((const.T, X_inde.T), axis=1)
    return X

#################################################
def gen_beta(params = [2, 3, 4, 5]):
    """
    Parameters
    ----------
    params: list
        input a list of beta parameters

    Returns
    ----------
    beta_array: ndnarry
        an array of beta coefficients, no nothing input, default value
        params = [2, 3, 4, 5]
    """
    beta_array = np.array(params)
    beta_array = beta_array[np.newaxis, :].T
    return beta_array

#################################################
def gen_u(n):
    """
    Parameters
    ----------
    params: int
        input a integer for the size of disturbance term

    Returns
    ----------
    u: ndnarry
        an array of disturbance term
    """
    u = np.random.randn(n)
    u = u[np.newaxis, :].T
    return u

#################################################
def ols(y, X):
    """
    Parameters
    ----------
    y, x: ndarray
        input ndarrays produced by gen_X

    Returns
    ----------
    beta_hat: ndnarry
        an array of estimated coefficients
    """
    beta_hat = np.linalg.inv(X.T@X)@X.T@y
    return beta_hat

    # TSS = np.linalg.norm(y)
    # ESS = np.linalg.norm(X@beta_hat)
    # RSS = np.linalg.norm(y-X@beta_hat)
#################################################
def proj_mat_P(X):
    """computing projection matrix P. Input X represents regressors.
    Parameters
    ----------
    X: ndarray
        input ndarrays produced by gen_X

    Returns
    ----------
    proj_matP: ndnarry
        Projection matrix P
    """
    proj_matP = X@np.linalg.inv(X.T@X)@X.T
    return proj_matP
#################################################
def proj_mat_M(X):
    """computing projection matrix M. Input X represents regressors.
    Parameters
    ----------
    X: ndarray
        input ndarrays produced by gen_X

    Returns
    ----------
    proj_matM: ndnarry
        Projection matrix M
    """
    proj_matM =  np.eye(X.shape[0]) - X@np.linalg.inv(X.T@X)@X.T
    return proj_matM
#################################################
def s_qr(resid, k):
    """
    Unbiased estimator of sigma square.
    Parameters
    ----------
    params: ndarray
        input an matrix-form ndarray of residuals

    Returns
    ----------
    beta_array: float
        return a float number
    """
    return np.sum(resid**2)/(len(resid)-k)

#################################################
def cov_beta_hat(resid, k, X):
    """
    Covariance matrix of estimated coefficients beta, standard
    Parameters
    ----------
    resid, k, X: ndarray, integer, ndarray

    Returns
    ----------
    cov_beta_hat, prin_diag: ndarray, ndarray

    """
    cov_beta_hat = np.sum(resid**2)/(len(resid)-k)*np.linalg.inv(X.T@X)
    cov_beta_hat_prin_diag = cov_beta_hat.diagonal()
    return (cov_beta_hat, cov_beta_hat_prin_diag)


import matplotlib.pyplot as plt
import numpy as np

def relu(x: float) -> float:
    if x > 0:
        return x
    else:
        return 0

def sigmoid(x: float) -> float:
    return float(1 / (1 + np.exp(-x)))

def main() -> None:
    # y = ReLU(wx + b)
    # y = sigmoid(wx + b)
    # shift = -b/w
    w = 1
    b = -4
    # shift = 2
    act_fn = sigmoid

    x = np.linspace(start=-10, stop=10, num=5000)
    y_act = np.array([act_fn(xi * w + b) for xi in x])
    y = np.array([act_fn(xi * 1 + 0) for xi in x])

    plt.figure(figsize=(8, 5))
    plt.grid(True)
    plt.plot(x, y, color="blue")
    plt.plot(x, y_act, color="red")
    plt.show()

if __name__ == "__main__":
    main()


import numpy as np
np.set_printoptions(suppress=True, precision=4)

import sys
import matplotlib.pyplot as plt
import sklearn.decomposition as decomp
import sklearn.linear_model as linear_model
import sklearn.datasets as sk_data
from sklearn.preprocessing import StandardScaler
import numpy.linalg as nla
import sklearn.svm as svm
import pandas as pd

def iqr_threshold_method(scores, margin):
    q1 = np.percentile(scores, 25, interpolation='midpoint')
    q3 = np.percentile(scores, 75, interpolation='midpoint')
    iqr = q3-q1
    lower_range = q1 - (1.5 * iqr)
    upper_range = q3 + (1.5 * iqr)
    lower_range = lower_range - margin
    upper_range = upper_range + margin
    return lower_range, upper_range

def RegressionOutlier(x_train, y_train,  margin=0, fit_intercept=True, normalize=False, copy_X=True, n_jobs=None):
    """Returns numpy array with data points labelled as outliers
    Parameters
    ----------
    x_train: numpy array like
        data points on which regression to be trained

    y_train: numpy array like
        lables of x_train

    margin: int, default=0
        Margin of error

    fit_intercept: bool, default=True
        Whether to calculate the intercept for this model. If set to False, no intercept will be used in calculations (i.e. data is expected to be centered).

    normalize: bool, default=False
        This parameter is ignored when fit_intercept is set to False. If True, the regressors X will be normalized before regression by subtracting the mean and dividing by the l2-norm. If you wish to standardize, please use sklearn.preprocessing.StandardScaler before calling fit on an estimator with normalize=False.

    copy_X: bool, default=True
        If True, X will be copied; else, it may be overwritten.

    n_jobs: int, default=None
        The number of jobs to use for the computation. This will only provide speedup for n_targets > 1 and sufficient large problems. None means 1 unless in a joblib.parallel_backend context. -1 means using all processors.
"""
    lr_train = linear_model.LinearRegression(fit_intercept=fit_intercept, normalize=normalize, copy_X=copy_X, n_jobs=n_jobs).fit(x_train, y_train)

    train_scores = (y_train - lr_train.predict(x_train))**2

    lower_range, upper_range = iqr_threshold_method(train_scores, margin)

    anomaly_points = []

    for i in range(len(train_scores)):
        if train_scores[i] < lower_range or train_scores[i] > upper_range:
            anomaly_points.append([x_train[i], y_train[i]])
    return anomaly_points

if __name__=='__main__':

    exam_data1 = np.array([[1, 2, 3, 4, 5],
                    [57, 70, 76, 84, 91]]).T
    x_train = np.array([1,2,3,4,5]).reshape(-1,1)
    x_test = np.array([57,70,99,84,91]).reshape(-1,1)
    res = RegressionOutlier(x_train, x_test, 0.5)
    print(res)


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

"""
    . Let N = 30

    . Let squares = { s_0, s_1, ..., s_899 } be the set of all squares in the grid,
        where s_i indicates the square at (i//N, i%N).

    . Let fleas = { f_0, f_1, ..., f_899 } be the set of all fleas
        where f_0 indicates the flea that is at position (i//N, i%N) at the beginning.

    . To simplify the notation, we let "index idx" indicate "the position (idx // N, idx % N)"

    . Due to the linearity of expectation, the expected number of empty squares is the sum of expected number of empty squares of EACH square; i.e.,
        E(N_empty) = sum_{s \in squares} {1 * P(s is empty) + 0 * [1 - P(s is empty)}
                   = sum_{s \in squares} P(s is empty)

    . To determine the probability of square s being empty,
        P(s is empty) = product_{t \in fleas} P(flea t is not in s after 50 iterations)

    . We use Markov process to determine the probability of each flea being at each square after 50 iterations. For each flea k:
        . Let T be the transistion matrix where
            T[i][j] = the probability of a flea at index i jumps to index j.
            so, T[i][j] = 1 / n_neighbors
        . Let S be the probability matrix where S[i] indicate the probability of flea k being at index i.
            So, at iteration 0, S[i] = 1 for i = k, and S[i] = 0 otherwise.
        . Let S_50 be the probability matrix where S[i] indicate the probability of flea k being at index i after 50 iterations.
            So, S_50 = S * matrix_power(T, 50)

    . So, P(s is empty) = (1 - S_50[s] of flea 0) * (1 - S_50[s] of flea 1) * ... * (1 - S_50[s] of flea 899)
"""

import numpy as np

N = 30
M = 50

def index_to_position(idx):
    return idx // N, idx % N

def position_to_index(x, y):
    return x * N + y

def get_neighbors(idx):
    x, y = index_to_position(idx)

    available_positions_idx = []
    for dx, dy in [(1, 0), (-1, 0), (0, 1), (0, -1)]:
        fx, fy = x + dx, y + dy
        if fx >= 0 and fx < N and fy >= 0 and fy < N:
            available_positions_idx.append(position_to_index(fx, fy))

    return available_positions_idx

if __name__ == "__main__":

    ans = 0

    T = np.zeros((N*N, N*N), dtype = np.float)
    for idx in range(N*N):
        neighbors_idx = get_neighbors(idx)
        n_neighbors_idx = len(neighbors_idx)
        for f_idx in neighbors_idx:
            T[idx][f_idx] = 1 / n_neighbors_idx

    TpM = np.linalg.matrix_power(T, M)

    P_empty = np.ones(N*N, dtype = np.float)

    S = np.zeros((1, N*N), dtype = np.float)

    for flea_idx in range(N*N):
        S = np.zeros((1, N*N), dtype = np.float)
        S[0][flea_idx] = 1.0
        S = S.dot(TpM)
        P_empty = np.multiply(P_empty, 1.0 - S)

    ans = np.sum(P_empty)

    print(ans)


import pandas as pd
import numpy as np
import plotly_express as px
import plotly.figure_factory as ff
import seaborn as sns
import statistics as st
data_file = pd.read_csv("c112-c113/savings.csv")

data_file_quant_saved = data_file["quant_saved"].tolist()
data_file_age = data_file["age"].tolist()

correlation = np.corrcoef(data_file_quant_saved, data_file_age)
print(correlation[0, 1])

data_file_plot_graph = px.scatter(data_file, x=data_file_age, y=data_file_quant_saved)
# data_file_plot_graph.show()

fig = ff.create_distplot([data_file_quant_saved], ["quantitysaved"], show_hist=False)
# fig.show()

# to draw a boxplot to check the outliers
ax = sns.boxplot(data=data_file, x= data_file["quant_saved"])

q1 = data_file["quant_saved"].quantile(0.25)
q3 = data_file["quant_saved"].quantile(0.75)
iqr = q3 -q1

lw  = q1-1.5*iqr
uw  = q3+1.5*iqr

print(lw ,uw)

new_data = data_file[data_file["quant_saved"]<uw]

new_savings = new_data["quant_saved"].tolist()

new_savings_mean = st.mean(new_savings)

new_savings_std = st.stdev(new_savings)

print("mean = "+str(new_savings_mean) + "  stdev = "+ str(new_savings_std))

# new_savings_displot = ff.create_distplot([new_savings], ["quantitysaved"], show_hist=False)
# new_savings_displot.show()

# temp_age = new_data[new_data.age != 0]

# new_age = temp_age["age"].tolist()

# temp_savings = temp_age["quant_saved"].tolist()


#!/usr/bin/env python

import math
import random

import numpy as np

def _ext_euc_alg(a, b):
    '''
    Performs Extended Euclidean Algorithm on two integers

            Parameters:
                    a : An integer
                    b : Another integer

            Returns:
                    (gcd, x, y) : tuple of GCD of (a,b) and (x,y) orthogonal to (a,b)
    '''
    if a == 0:
        return b, 0, 1

    gcd,x1,y1 = _ext_euc_alg(b % a, a)

    # Update x and y using results of recursive call
    x = y1 - (b // a) * x1
    y = x1

    return gcd,x,y

def _inv_mult(a, m):
    '''
    Find modular inverses using EEA

            Parameters:
                    a : Inverse to be found
                    m : Modulus – must have (a, m) == 1

            Returns:
                    inv : Modular inverse of a mod m
    '''
    gcd, x, _ = _ext_euc_alg(a, m)

    if gcd == 1:
        return (x + m) % m

def _mod_pow(a, e, m):
    '''
    Compute modular exponentation under modular arithmetic

            Parameters:
                    a : Base
                    e : Exponent
                    m : Modulus

            Returns:
                    b : Value of a^e % m
    '''
    a, b = a % m, 1

    if a == 0: return 0

    while e > 0:
        if (e & 1) == 1:
            b = (a * b) % m

        e >>= 1
        a = (a * a) % m

    return b

def RSA_setup(p,q):
    '''
    Sets up parameters for RSA algorithm

            Parameters:
                    p : Large prime
                    q : Another large prime

            Returns:
                    (n, e, d) : RSA modulus, public key, and private key
    '''
    n = p * q
    phi_n = (p - 1) * (q - 1)

    # select public key s.t. (e, phi_n) == 1
    found = False

    while not found:
        e = random.randint(1, phi_n)
        if math.gcd(e, phi_n) == 1:
            found = True

    d = _inv_mult(e, phi_n)

    return n, e, d

def RSA_encrypt(m, e, n):
    '''
    Encrypts messages with RSA algorithm

            Parameters:
                    m : Integer representation of message – must be between 1 and n
                    e : Public key – selected as per RSA_setup
                    n : Modulus – selected as per RSA_setup

            Returns:
                    M : Encrypted message
    '''
    return _mod_pow(m, e, n)

def RSA_decrypt(M, d, n):
    '''
    Decrypts messages with RSA algorithm

            Parameters:
                    M : Encrypted message – calculated as per RSA_encrypt
                    d : Private key – selected as per RSA_setup
                    n : Modulus – selected as per RSA_setup

            Returns:
                    m : Plaintext message
    '''
    return _mod_pow(M, d, n)

class PairValEnc:
    def __init__(self, p, q):
        '''
        Initializes Pair Value Encryption class – performs same setup as RSA algorithm

                Parameters:
                        p : Large prime
                        q : Another large prime

                Returns:
                        None
        '''
        n = p * q
        phi_n = (p - 1) * (q - 1)

        found = False

        while not found:
            e = random.randint(1, phi_n)
            if math.gcd(e, phi_n) == 1:
                found = True

        d = _inv_mult(e, phi_n)

        self.n = n
        self.phi_n = phi_n
        self.e = e
        self.d = d

    def load(self, values, mapping):
        '''
        Store problem space and mapping from problem to plaintext space in object

                Parameters:
                        values : list equal to problem space
                        mapping : dict object converting values in problem space to plaintext space

                Returns:
                        None
        '''
        self.values = values
        self.mapping = mapping
        self.inv_mapping = {v:k for k, v in mapping.items()}

    def encrypt(self, x):
        '''
        Map an input from the problem space to the plaintext space, and encrypt it

                Parameters:
                        x : Value in problem space

                Returns:
                        <enc> : Encrypted value of input
        '''
        if x in self.values:
            return _mod_pow(self.mapping[x], self.e, self.n)

    def compare(self, x, y):
        '''
        Compare pairs of encrypted values

                Parameters:
                        x : Encrypted value
                        y : Another encrypted value

                Returns:
                        <cmp> : Binary comparison, returning True if the inputs are matchable and False if not
        '''
        return ((x * y) % self.n) == 1

    def decrypt(self, x, d):
        '''
        Verify the decryption key and decrypt a value from the encryption space to the problem space

                Parameters:
                        x : Encrypted value
                        d : Private key

                Returns:
                        <dec> : Decrypted value of input
        '''
        if d == self.d:
            return self.inv_mapping[_mod_pow(x, self.d, self.n)]

def prep_example():
	'''
	Create the problem space and problem to plaintext mapping necessary for example in paper

			Parameters:
					None

			Returns:
					(X, X_to_Z) : Problem space as <list>, and problem to plaintext mapping as <dict>
	'''
	X = list(range(16))
	match = lambda x, y: int(x ^ y == 15)

	n = 33

	# Step 1, find a list of matches in X/~
	X_matches = []
	x = len(X)
	for i in range(x):
	    for j in range(i,x):
	        if match(i,j):
	            X_matches.append((i,j))

	# Step 2, find |X/~| pairs of modular inverses from Z/33Z
	Z_matches = []

	i = 1
	while len(Z_matches) < len(X_matches):
	    j = _inv_mult(i,n)

	    if j is None:
	        i += 1
	        continue

	    Z_matches.append((i,j))
	    i += 1

	# Step 3, map values over
	X_to_Z = {}

	for i, (x,y) in enumerate(X_matches):
	    X_to_Z[x] = Z_matches[i][0]
	    X_to_Z[y] = Z_matches[i][1]

	return X, X_to_Z


# Computational Linear Algebra Ep#9 Non-Square Lu Decomposition
# By: Nick Space Cowboy

import numpy as np
from lin_alg import lin_alg

if __name__ == "__main__":
	# generate A matrix
	A = np.array(np.random.randint (1,100,(3,7)), dtype=np.float64)
	print("A: ")
	print(A)
	# perform LU decomposition with our LinAlg LU function
	L, U = lin_alg.LU(A)
	print("L: ")
	print(L)
	print("U: ")
	print(U)
	print("Check: ")
	# Check that LU=A
	print((A - np.dot(L,U)).round())


import logging
import numpy
import math
import sys

"""
Return true if x is less than or equal to y (with respect to tolerance tol).
"""
def float_le(x, y, tol):
    #TODO: handle degenerate cases (inf e nan)
    return (x == y) or (x-y) <= max(abs(x), abs(y))*tol

"""
lam = lambda
mu = mu
m = number of servers
"""
def p0(lam,mu,c):

    sum = 0.0
    for x in range(0,c):
        f1 = math.pow((lam/mu),x)
        f2 = math.factorial(x)
        val = f1*f2
        sum += val
    f3 = 1.0/math.factorial(c)
    f4 = math.pow((lam/mu),c)
    f5 = (c*mu)/(c*mu-lam)
    sum += f3*f4*f5

    prob0 = 1.0/sum
    return prob0

def pQueue(lam,mu,c):

    f1 = c*math.pow(lam/mu,c)
    f2 = math.factorial(c)
    f3 = c-(lam/mu)
    f4 = p0(lam,mu,c)
    pQ = 1-(f1/(f2*f3))*f4
    #print "PQueue - f1:",f1,"f2:",f2,"f3:",f3,"f4:",f4,"pQ:",pQ
    return pQ

def responseTimeCDF(lam,mu,c,t):

    rho = lam/(mu*c)
    if rho <= 0 or rho >=1 :
        logging.warning( "With c = %d, rho is %f which is unfeasible", c, rho)
        return 0
    retval = 0.0
    f1 = (lam-c*mu+mu*pQueue(lam,mu,c))/(lam-(c-1.0)*mu)
    f2 = 1-math.exp(-mu*t)
    f3 = ((1.0-pQueue(lam,mu,c))*mu)/(lam-(c-1)*mu)
    f4 = 1-math.exp(-(c*mu-lam)*t)
    retval = f1*f2+f3*f4
    #print "responseTimeCDF - f1:",f1,"f2:",f2,"f3:",f3,"f4:",f4,"retval:",retval
    return retval

def responseTimeCDF2(lam,mu,c,t):
    pQ = pQueue(lam,mu,c)
    f1 = pQ*(1-math.exp(-mu*t))
    f2 = ((c*mu-lam)/((c-1)*mu-lam))*(1-math.exp(-mu*t))
    f3 = ((mu)/((c-1)*mu-lam))*(1-math.exp(-(c*mu-lam)*t))
    f4 = (1-pQ)*(f2-f3)
    retval = f1+f4
    return retval

def pi0(lam,mu,m):
    rho = lam/(m*mu)
    part1 = (math.pow(m*rho,m)/math.factorial(m))*(1/(1-rho))
    part2=0.0
    for k in range(0,m):
        part2 +=math.pow(m*rho,k)/math.factorial(k)
    val = 1/(part1+part2)
    return val

def Pm(lam,mu,m):
    rho = lam/(m*mu)
    pai0 = pi0(lam,mu,m)
    pm = (math.pow(m*rho,m)/(math.factorial(m)*(1-rho)))*pai0
    return pm

"""
Compute the average response time for an M/M/m model:
 \frac{C(m, \lambda/\mu)}{m \mu - \lambda} + \frac{1}{\mu}
where:
- C(m, \lambda/\mu) is the Erlang's C formula:
  C(m,\lambda/\mu) = \frac{\left(\frac{(m\rho)^{m}}{m!}\right)\left(\frac{1}{1-\rho}\right)}{\sum_{k=0}^{m-1}\frac{(m\rho)^{k}}{k!} + \left(\frac{(m\rho)^m}{m!}\right)\left(\frac{1}{1-\rho}\right)}
- \rho is the system utilization:
  \rho = \frac{\lambda}{m\mu}
.

Parameters:
- arrival rate \lambda=lam,
- service rate \mu=mu, and
- number of server m=m
.
"""
def avgRespTime_MMC(lam,mu,m):

    rho = lam / (m * mu)
    if m==1:
        val = (1.0/mu)/(1.0-rho)
    else:
        pm = Pm(lam,mu,m)
        avgK = m*rho+(rho/(1-rho))*pm
        val = avgK/lam
    return val

def getMu_MM1(lam,r,perc):

    val = (lam*r-math.log(1-perc))/r
    return val

def respTimeCDF_MM1(lam,mu,r):
    return (1-math.exp(-mu*(1-lam/mu)*r))

def compute(lam, mu, r, tol):

    #error = 0.0000005

    #set initial mu

    # applies the formula with an increasing number m of
    # servers, all having the same service rate mu, until
    # the resoponse time val is <= than the limit r
    m = 0
    #for m in range(1,101):
    while True:
        m += 1

        # Check for stability
        if (lam/(m*mu)) >= 1:
            #print "[percentileTrivedi] model NOT stable"
            continue

        val = avgRespTime_MMC(lam,mu,m)

        # Check for target response time
        #if val <= r:
        #if (r-val)/r <= tol:
        if float_le(val, r, tol):
            logging.debug("lambda: %f, mu: %f, m: %d, RT: %f, limit: %f", lam, mu, m, val, r)
            break
        logging.debug("lambda: %f, mu: %f, m: %d --> RT: %f > limit: %f", lam, mu, m, val, r)

    return m

#if __name__ == "__main__":
def main(lam,mu,r,tol,debug=True):
   # if(len(sys.argv)<4):
   #     print "not enough input parameters"
   #     sys.exit()

    #lam = float(sys.argv[1])
    #mu = float(sys.argv[2])
    #r = float(sys.argv[3])

    logging.basicConfig(stream=sys.stderr, format='[%(filename)s:%(funcName)s:%(levelname)s>> %(message)s', level=logging.DEBUG if debug else logging.INFO)

    logging.debug("Input parameters: lambda = %f, mu =%f, r = %f, tol = %f", lam, mu, r, tol)

    #if((int(lam)/mu)>=1):
    if(r < 1/mu):
        logging.warning("model NOT feasible: response time < service time")
        #sys.exit()
        return -1

    return compute(lam,mu,r,tol)


import numpy as np

# P -> N
def p2n(p):
    '''Compute the expected first return time from state i to state j given the transition probabilities.'''
    # n_xy = p_xy + sum_{z ≠ y} p_xz (n_zy + 1)
    # n_xy - sum_{z ≠ y} p_xz n_zy = 1
    # Flatten n_xy by mapping (x, y) to x * d + y
    d = p.shape[0]
    a = np.eye(d * d)
    for x in range(d):
        for y in range(d):
            i = x * d + y
            for z in range(d):
                if z != y:
                    j = z * d + y
                    a[i, j] -= p[x, z]
    b = np.ones(d * d)
    # print(a)
    r = np.linalg.solve(a, b)
    return r.reshape((d, d))

# N -> P
def n2p(n):
    '''Compute the transition probability from state i to state j given the expected first return times.'''
    return n @ np.linalg.inv(n - np.diagflat(np.diagonal(n)) + 1)

if __name__ == '__main__':
    import math

    # Transition
    p0 = np.array([
        [0.9, 0.1],
        [0.01, 0.99],
    ])

    # First return times
    n0 = np.array([
        [0.9 * 1 + 0.1 * (100 + 1), 10],
        [100, 0.99 * 1 + 0.01 * (10 + 1)],
    ])

    print(np.abs(p2n(p0) - n0).max())
    print(np.abs(n2p(n0) - p0).max())

    # Random transitions
    # d = 5
    # p = np.random.dirichlet([1] * d, d)
    # Simple deterministic transitions
    p1 = np.array([
        [0, 1, 0],
        [0, 0, 1],
        [1, 0, 0],
    ])
    print(p2n(p1))
    print(np.abs(n2p(p2n(p1)) - p1).max())

    # Singular matrix => Some n_xy are inf
    # p = np.array([
    #     [0, 1, 0],
    #     [1, 0, 0],
    #     [1, 0, 0],
    # ])
    # n = np.array([
    #     [2, 1, math.inf],
    #     [1, 2, math.inf],
    #     [1, 2, math.inf],
    # ])
    # print(n2p(n))
    # p = np.array([
    #     [0, 1, 0],
    #     [1, 0, 0],
    #     [0, 0, 1],
    # ])
    # n = np.array([
    #     [2, 1, math.inf],
    #     [1, 2, math.inf],
    #     [math.inf, math.inf, 1],
    # ])
    # print(p2n(p))
    # print(n2p(n))


import time
from random import randrange
import matplotlib.pyplot as plt
import numpy as np

# O(N)
def findmin1(data):
    lowest = data[0]
    for i in data:
        if i < lowest:  # di eksekusi selama n
            lowest = i  # dieksekusi 1 kali
    return lowest  #   dieksekusi 1 kali

# O(N^2)
def findmin2(data):
    lowest = data[0]
    for i in data:  # N
        isSmallest = True
        for j in data:  # N
            if i > j:
                isSmallest = False
            if isSmallest:
                lowest = i
    return lowest

def findmin3(data):
    return min(data)

#example_list=[5,3,6,4,1]
plt.xlabel("size")
plt.ylabel("time")
#print(findmin1(example_list))
#print(findmin2(example_list))
x1 = []
y1 = []
for listSize1 in range(1000, 10001, 1000):
    example_list = [randrange(10000) for x in range(listSize1)]
    start_time = time.time()
    print(f'minimum value: {findmin1(example_list)}')
    end_time = time.time()
    et = end_time - start_time
    print(f'size list: {listSize1}, time execution: {et:5f}')
    x1.append(listSize1)
    y1.append(et)
print()

x2 = []
y2 = []
for listSize2 in range(1000, 10001, 1000):
    example_list = [randrange(100000) for x in range(listSize2)]
    start_time = time.time()
    print(f'minimum value: {findmin2(example_list)}')
    end_time = time.time()
    et2 = end_time - start_time
    print(f'size list: {listSize2}, time execution: {et2:.5f}')
    x2.append(listSize2)
    y2.append(et2)
print()

x3 = []
y3 = []
for listSize3 in range(1000, 10001, 1000):
    example_list3 = [randrange(100000) for x in range(listSize3)]
    start_time = time.time()
    print(f'minimum value: {findmin3(example_list3)}')
    end_time = time.time()
    et3 = end_time - start_time
    print(f'size list: {listSize3}, time execution: {et3:.5f}')
    x3.append(listSize3)
    y3.append(et3)
print()
tx = y1[5:] + y2[:5]
plt.plot(x1, tx, 'g-', alpha=1)
plt.plot(x2, tx, 'r-', alpha=1)
# Label the axes and provide a title
plt.title("haha")
plt.grid(True)
plt.xlabel("size")
plt.ylabel("time")
plt.show()


import sympy
import math
from prettytable import PrettyTable
x = sympy.Symbol('x')
table = PrettyTable()

iters = []
xn = []
funcF = []
funcD1 = []
funcD2 = []
errorTot = []

def secant(tol,x0,niter):
    if(tol >= 0):
        if (ite > 0):
            fx0 = f.evalf().subs({x:x0})
            if(fx0 != 0):
                fx1 = f.evalf().subs({x:x0}).evalf()
                funcF.append(fx1)
                error = tol+1
                errorTot.append(0)
                iters.append(0)
                xn.append(x0)
                cont = 0
                aux0 = x0
                while(fx1 != 0 and den != 0 and error > tol and cont < niter):
                    xn.append(aux1)
                    aux2 = aux1-f.evalf().subs({x:aux1}).evalf()*(aux1-aux0)/den
                    error = abs(aux2-aux1)
                    errorTot.append(error)
                    aux0 = aux1
                    fx0 = fx1
                    aux1 = aux2
                    fx1 = f.evalf().subs({x:aux1}).evalf()
                    funcF.append(fx1)
                    cont = cont + 1
                    iters.append(cont)
                if ( fx1 == 0):
                    print(str(aux1)+" Is a root")
                elif (error < tol):
                    print(str(aux1)+" Is an aproximate root")
                else:
                    print("The method failed")
                table.add_column("n",iters)
                table.add_column("xn",xn)
                table.add_column("f(xn)",funcF)
                table.add_column("error",errorTot)
                print(table)
            else:
                print("x0 Is a root",x0)
        else:
            print("Wrong iterates")
    else:
        print("Tolerance < 0")

if __name__ == "__main__":
    x = sympy.Symbol('x')
    symbols = {'e':math.e,'cos':sympy.cos,'sin':sympy.sin,'ln':sympy.ln}
    function  = input("Enter the function f(x) = ")
    function = sympy.sympify(function,locals = symbols)
    f = function
    fd1 = sympy.diff(f)
    fd2 = sympy.diff(fd1)
    x0 = float(input("Enter the x0 point: "))
    tol = float(input("Enter the tolerance: "))
    ite = int(input("Enter N iteraters: "))
    secant(tol,x0,ite)


import math
import numpy as np
from sklearn.decomposition import PCA

def ci95_ellipse(data, type="pop"):
    """ Construct a 95% confidence ellipse using PCA.

    Parameters
    ----------
    data : array-like, shape = [n_samples, 2]
        data[:,0] must represent x coordinates
        data[:,1] must represent y coordinates

    type : string, optional (default='pop')
        It must be 'pop' or 'mean'

    Returns
    -------
    ellipse: array-like, shape = [100, 2]
        ellipse[:,0] represents x coordinates of ellipse
        ellipse[:,1] represents y coordinates of ellipse

    outside: array-like, shape = [n_samples, 1]
        returns an 1d array (of 0/1) with length n_samples
        0 : ith sample is outside of ellipse
        1 : ith sample is inside of ellipse
    """

    # Build and fit PCA model
    pca = PCA()
    pca.fit(data)
    coeff = pca.components_
    score = pca.transform(data)
    eigvals = pca.explained_variance_

    # Calculate rotation angle
    phi = math.atan2(coeff[0, 1], coeff[0, 0])

    # This angle is between -pi and pi.
    # Let's shift it such that the angle is between 0 and 2pi
    if phi < 0:
        phi += 2 * math.pi

    # Get the coordinates of the data mean
    n = len(data)
    m = np.mean(data, axis=0)
    x0 = m[0]
    y0 = m[1]

    # Get the 95% confidence interval error ellipse
    # inverse of the chi-square cumulative distribution for  p = 0.05 & 2 d.f. = 5.9915
    chisquare_val = 5.9915
    if type is "pop":
        a = math.sqrt(chisquare_val * eigvals[0])
        b = math.sqrt(chisquare_val * eigvals[1])
    elif type is "mean":
        a = math.sqrt(chisquare_val * eigvals[0] / n)
        b = math.sqrt(chisquare_val * eigvals[1] / n)
    else:
        raise ValueError("type has to be 'pop' or 'mean'.")

    # the ellipse in x and y coordinates
    theta_grid = np.linspace(0, 2 * math.pi, num=100)
    ellipse_x_r = a * np.cos(theta_grid)
    ellipse_y_r = b * np.sin(theta_grid)

    # Define a rotation matrix
    R = np.array([[np.cos(phi), np.sin(phi)], [-np.sin(phi), np.cos(phi)]])
    # let's rotate the ellipse to some angle phi
    r_ellipse = np.dot(np.vstack((ellipse_x_r, ellipse_y_r)).T, R)

    # Draw the error ellipse
    x = r_ellipse[:, 0] + x0
    y = r_ellipse[:, 1] + y0
    ellipse = np.stack((x, y), axis=1)

    outside = []
    for i in range(len(score)):
        metric = (score[i, 0] / a) ** 2 + (score[i, 1] / b) ** 2
        if metric > 1:
            outside.append(1)
        else:
            outside.append(0)

    return ellipse, outside


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Jun 17 17:09:49 2018

@author: deepayanbhadra
"""
import numpy as np
def buildmat(m,n,condNumber):
    A = np.random.randn(m, n)
    np.linalg.cond(A)
    U, S, V = np.linalg.svd(A)
    S = np.array([[S[j] if i==j else 0 for j in range(n)] for i in range(m)])
    S[S!=0]= np.linspace(condNumber,1,min(m,n))
    A=U.dot(S).dot(V)
    return A
# For a 3x5 matrix

m,n,condNumber = 3,5,2
print("The 3x5 matrix A and the condition no. are\n")
A = buildmat(m,n,condNumber)
print(np.matrix(A),"\n")
print(np.linalg.cond(A),"\n")

# For a 5x4 matrix

m,n,condNumber = 5,4,4
print("The 5x4 matrix A and the condition no. are\n")
A = buildmat(m,n,condNumber)
print(np.matrix(A),"\n")
print(np.linalg.cond(A),"\n")


import numpy as np

M_LAPLACIEN_S = np.array([[-1,-1,-1],[-1,8,-1],[-1,-1,-1]])
M_LAPLACIEN = np.array([[0,1,0],[1,-4,1],[0,1,0]])
M_LAPLACIEN_DIAG = np.array([[1,1,1],[1,-8,1],[1,1,1]])
M_LAPLACIEN_OPP = -M_LAPLACIEN
M_LAPLACIEN_DIAG_OPP = -M_LAPLACIEN_DIAG
M_SOBEL_1 = np.array([[-1,-2,-1],[0,0,0],[1,2,1]])
M_SOBEL_2 = np.array([[-1,0,1],[-2,0,2],[-1,0,1]])

def D(u, v, M, N):
    return np.sqrt( np.power(u - (M/2), 2) + np.power(v - (N/2), 2) )

def M_masque_moyenneur(n):
    return np.ones((n,n))/9

def H_passe_bas_ideal(M, N, D0 = 1):
    H = np.zeros((M,N))
    for u in range(M):
        for v in range(N):
            H[u,v] = 1 if D(u, v, M, N) <= D0 else 0
    return H

def H_passe_bas_butterworth(M, N, D0 = 1, n = 1):
    H = np.zeros((M,N))
    for u in range(M):
        for v in range(N):
            H[u,v] = 1 / (1 + np.power(D(u, v, M, N) / D0, 2*n))
    return H

def H_passe_bas_gaussien(M, N, D0 = 1):
    H = np.zeros((M,N))
    for u in range(M):
        for v in range(N):
            H[u,v] = np.exp(-(D(u, v, M, N)**2) / (2 * (D0**2)))
    return H

def H_filtre_laplacien(M, N):
    H = np.zeros((M,N))
    for u in range(M):
        for v in range(N):
            H[u,v] = -((u - (M/2))**2 + (v - (N/2))**2)
    return H

def get_filtre_rehaussement(H, a, b, M, N, *args, **kwargs):
    if a < 0:
        raise Exception ("a compris généralement entre 0.25 et 0.5")
    if b < a:
        raise Exception(" b has to be greater than a")
    if a < 0.25 or a > 0.5:
        print("Attention ! a est compris généralement entre 0.25 et 0.5")
    if b < 1.5 or b > 2 :
        print("Attention ! b est compris généralement entre 1.5 et 2")
    return a + (b * (1-H(M, N,*args, **kwargs))) # 1 - H car mes H sont des passe bas


"""
bayesian analysis for population of marked items
Known total population: Nt We sample Nb, and find Nc of Nb marked
what is total population of marked items? based on same hypergeometric distribution as TagAndRelease
"""
import numpy as np
import matplotlib.pyplot as plt
from math import lgamma,exp
from SciInf_utilities import *
import sys
#-------------------------------
#
print("\n bayesian analysis for population of marked items")
print(" Known total population: Nt We sample Nb, and find Nc of Nb marked")
print(" what is total population of marked items? based on same hypergeometric distribution as TagAndRelease \n")
#
# test data
#n_tot = 15
#n_got = 10
#n_lab = 3
if(len(sys.argv) == 4):
  n_tot = int(sys.argv[1])
  n_got = int(sys.argv[2])
  n_lab = int(sys.argv[3])
else:
  n_tot = int(input('# total> '))
  n_got = int(input('# sampled> '))
  n_lab = int(input('# of samples that are marked> '))
print('from total of: {:8d}   # sampled: {:8d}   of which {:8d}   are marked'.format(n_tot,n_got,n_lab))
#
# range of n_lab to explore
#
n_min = n_lab
n_not = n_tot - n_got
n_max = n_lab + n_not
n_range = n_max + 1 - n_min
print('unsampled: {:8d}  # marked min: {:8d} and max: {:8d} '.format(n_not,n_min,n_max))
#
# generate pdf, cdf
#
n_axis = np.zeros(n_range,int)
n_pdf = np.zeros(n_range)
n = n_min
for i in range(n_range):
  n_axis[i] = n
  n1 = n + 1
  n2 = (n_tot - n) + 1
  n3 = (n - n_lab) + 1
  n4 = (n_max - n) + 1
  lpdf = lgamma(n1) + lgamma(n2) - lgamma(n3) - lgamma(n4)
  n_pdf[i] = lpdf
  n += 1
lpdf_max = max(n_pdf)
n_pdf = n_pdf - lpdf_max
n_pdf = np.exp(n_pdf)
#
n_cdf = pdf_to_cdf(n_axis,n_pdf,discrete=True)
f_lab = float(n_lab)/float(n_got)
n_mle = int(n_tot*f_lab)
print('fraction of sample marked: {:12.5f} Max. Like. Est of total Marked: {:6d}'.format(f_lab,n_mle))
summarize(n_axis,n_pdf,n_cdf,discrete=True,title='total # marked')

if(MAKEPLOT):
  plt.figure()
  plt.scatter(n_axis,n_pdf,color='green',marker='o')
  plt.scatter(n_axis,n_cdf,color='red',marker='s')
  plt.ylim((0.,1.2))
  plt.xlabel('N')
  plt.ylabel('prob(N)')
  #plt.title('posterior pdf,cdf for size of marked population')
  #plt.xscale('log')
  plt.grid(True)
  plt.show()


#!/usr/bin/python3

from sympy import symbols, cos, sin, pi, simplify
from sympy.matrices import Matrix
import numpy as np

### Create symbols for joint variables which are commonly represented by "q"
### Joint variable "q" is equal to "th" or "d" depending if the joint is revolute or prismatic

q1, q2, q3, q4 = symbols('q1:5') # remember slices do not include the end value

# unrelated symbols can be defined like this:
A, R, O, C = symbols('A R O C')

# Conversion Factors
rtd = 180./np.pi # radians to degrees
dtr = np.pi/180. # degrees to radians

R_x = Matrix([[ 1,              0,        0],
              [ 0,        cos(q1), -sin(q1)],
              [ 0,        sin(q1),  cos(q1)]])

R_y = Matrix([[ cos(q2),        0,  sin(q2)],
              [       0,        1,        0],
              [-sin(q2),        0,  cos(q2)]])

R_z = Matrix([[ cos(q3), -sin(q3),        0],
              [ sin(q3),  cos(q3),        0],
              [ 0,              0,        1]])

print("Rotation about the X-axis by 45-degrees")
print(R_x.evalf(subs={q1: 45*dtr}))
print("Rotation about the y-axis by 45-degrees")
print(R_y.evalf(subs={q2: 45*dtr}))
print("Rotation about the Z-axis by 30-degrees")
print(R_z.evalf(subs={q3: 30*dtr}))


from __future__ import division
import numpy as np
import scipy as sp
import matplotlib.pyplot as plt
import pandas as pd

def matmul(A, B):
    # multiply two 2-D square matrices together
    n = A.shape[0]
    U = np.array([[0] * n for i in range(n)], dtype=np.float64)
    for i in range(0, n):
        for j in range(0, n):
            U[i, j] = np.dot(A[i], B.T[j])
    return U

def rel_residual(A, x_cap, x, b):
    # returns 2-norm relative residual of a linear system
    res = np.linalg.norm(b - np.array([np.dot(A[i], x_cap) for i in range(n)]), ord=2)
    rel_res = res / (np.linalg.norm(A, ord=2) * np.linalg.norm(x, ord=2))
    return rel_res

def rel_error(x_cap, x):
    # returns 2-norm relative error
    return np.linalg.norm(x_cap - x, ord=2) / np.linalg.norm(x, ord=2)

n = 100
np.random.seed(1729)
A = np.random.randn(n, n)

x = np.ones(n)
b = np.array([np.dot(A[i], x) for i in range(n)])

DA = A
Db = b
x_cap = np.linalg.solve(DA, Db)
rel_err_1 = rel_error(x_cap, x)
rel_res_1 = rel_residual(DA, x_cap, x, Db)
cno_1 = np.linalg.cond(DA, p=2)

D = np.diag(2 * np.ones(n))
DA = matmul(D, A)
Db = np.array([np.dot(D[i], b) for i in range(n)])
x_cap = np.linalg.solve(DA, Db)
rel_err_2 = rel_error(x_cap, x)
rel_res_2 = rel_residual(DA, x_cap, x, Db)
cno_2 = np.linalg.cond(DA, p=2)

D = np.diag(np.linspace(1, 100, 100))
DA = matmul(D, A)
Db = np.array([np.dot(D[i], b) for i in range(n)])
x_cap = np.linalg.solve(DA, Db)
rel_err_3 = rel_error(x_cap, x)
rel_res_3 = rel_residual(DA, x_cap, x, Db)
cno_3 = np.linalg.cond(DA, p=2)

D = np.diag(np.linspace(1, 10000, 100))
DA = matmul(D, A)
Db = np.array([np.dot(D[i], b) for i in range(n)])
x_cap = np.linalg.solve(DA, Db)
rel_err_4 = rel_error(x_cap, x)
rel_res_4 = rel_residual(DA, x_cap, x, Db)
cno_4 = np.linalg.cond(DA, p=2)

D = np.diag(2**-np.arange(-n//2, n//2, dtype=np.float64))
DA = matmul(D, A)
Db = np.array([np.dot(D[i], b) for i in range(n)])
x_cap = np.linalg.solve(DA, Db)
rel_err_5 = rel_error(x_cap, x)
rel_res_5 = rel_residual(DA, x_cap, x, Db)
cno_5 = np.linalg.cond(DA, p=2)

data = pd.DataFrame({"(i)" : [rel_err_1, rel_res_1, cno_1], "(ii)" : [rel_err_2, rel_res_2, cno_2],\
              "(iii)" : [rel_err_3, rel_res_3, cno_3], "(iv)" : [rel_err_4, rel_res_4, cno_4],\
              "(v)" : [rel_err_5, rel_res_5, cno_5]}, index=["Relative Error", "Relative Residual",\
                                                             "Condition Number"]).T
data.index.name = "Part"
data.columns.name = "Statistic"

print(data.to_string())

# print(data.to_latex())


import numpy as np
import matplotlib.pyplot as plt

def create_2d_data_points():
    x = np.linspace(-2, 2, 100)
    y = x**5 - 6 * x**3 + 6 * x + 4 * np.cos(x*6) + 8
    return x, y

def print_poly_cobf_formula(x, y, degree):
    degree += 1
    x_extended = add_column_with_ones(x)

    for i in range(2, degree):
        x_extended = np.c_[x_extended, x**i]

    x_trans = x_extended.transpose()
    w = np.linalg.solve(x_trans.dot(x_extended), x_trans.dot(y))

    degree = len(w)
    print(f"\nCurve of best fit with degree {degree-1}:\ny = ", end = "")

    for i in range(degree):
        print(f"{w[i]:.3f}", end = "")
        if i >= 1:
            print("x", end = "")
        if i >= 2:
            print(f"^{i}", end = "")
        if i < degree - 1:
                print(" + ", end = "")
    print()

def add_column_with_ones(x):
    if len(x.shape) == 1:
        x = np.reshape(x, (x.shape[0], 1))
    return np.hstack((np.ones((x.shape[0], 1)).astype(int), x))

def poly_curve_of_best_fit(x, y, degree):
    degree += 1
    x_extended = add_column_with_ones(x)

    for i in range(2, degree):
        x_extended = np.c_[x_extended, x**i]

    x_trans = x_extended.transpose()
    w = np.linalg.solve(x_trans.dot(x_extended), x_trans.dot(y))

    return sum([w[i]*x_extended[:,i] for i in range(x_extended.shape[1])])

def mae(yhat, y):
    return (np.absolute(yhat - y)).mean()

def rmse(yhat, y):
    diff = yhat - y
    return diff.dot(diff)**0.5

def r_squared(yhat, y):
    diff_hat = yhat - y
    diff_bar = y.mean() - y
    return 1 - (diff_hat.dot(diff_hat) / diff_bar.dot(diff_bar))

def r_squared_adj(yhat, y, degree):
    n = 100
    p = degree
    return 1 - ((1 - r_squared(yhat, y)) * (n - 1)) / (n - p - 1)

def plot_metric(x, y, method):
    values = np.empty((14,))

    for degree in range(1, 15):
        cobf = poly_curve_of_best_fit(x, y, degree)
        if method == "MAE":
            values[degree-1] = (mae(cobf, y))
        elif method == "RMSE":
            values[degree-1] = (rmse(cobf, y))
        elif method == "R squared":
            values[degree-1] = (r_squared(cobf, y))
        elif method == "Adjusted R squared":
            values[degree-1] = (r_squared_adj(cobf, y, degree))

    plt.title(f"Method: {method}")
    plt.plot(np.linspace(1, 14, 14), values)
    plt.show()

def plot_all_metrics(x, y):
    values = [np.empty((14,)) for _ in range(4)]
    label_text = ["MAE", "RMSE", "R2", "Adjusted R2"]

    for degree in range(1, 15):
        cobf = poly_curve_of_best_fit(x, y, degree)
        values[0][degree-1] = (mae(cobf, y))
        values[1][degree-1] = (rmse(cobf, y))
        values[2][degree-1] = (r_squared(cobf, y))
        values[3][degree-1] = (r_squared_adj(cobf, y, degree))

    for i in range(2):
        values[i] -= 2 * (values[i] - values[i].mean())
        values[i] /= max(values[i])

    for i in range(4):
        plt.plot(np.linspace(1, 14, 14), values[i], label=label_text[i])

    plt.legend()
    plt.show()

def plot_data_points(x, y):
    plt.plot(x, y)
    plt.ylim((-4, 20))
    plt.show()

def plot_all_curves(x, y):
    curves = [poly_curve_of_best_fit(x, y, degree) for degree in range(1, 15)]
    plt.plot(x, y)
    for cobf in curves:
        plt.plot(x, cobf)
    plt.ylim((-4, 20))
    plt.show()

def plot_curve_with_metrics(x, y, degree):
    cobf = poly_curve_of_best_fit(x, y, degree)
    plt.text(1.07, 15, "MAE:\nRMSE:\nR2:\nR2-adj:              ", bbox={'facecolor': 'grey', 'alpha': 0.1, 'pad': 5})
    plt.text(1.53, 15, calc_metrics(cobf, y, degree))
    plt.text(-0.75, 20.5, f"Polynomial with degree {degree}")
    plt.plot(x, y)
    plt.plot(x, cobf)
    plt.ylim((-4, 20))
    plt.show()

def calc_metrics(cobf, y, degree):
    text = f"{mae(cobf, y):.5}\n{rmse(cobf, y):.5}\n{r_squared(cobf, y):.5}\n{r_squared_adj(cobf, y, degree):.5}"
    return text

def print_metrics(x, y):
    for degree in range(1, 15):
        print("*********************************")
        print(f"Calculating model with degree {degree}.\n")
        cobf = poly_curve_of_best_fit(x, y, degree)

        print(f"MAE:\t{mae(cobf, y):.5}")
        print(f"RMSE:\t{rmse(cobf, y):.5}")
        print(f"R2:\t{r_squared(cobf, y):.5}")
        print(f"R2-adj:\t{r_squared_adj(cobf, y, degree):.5}\n")

x, y = create_2d_data_points()

print_metrics(x, y)

plot_data_points(x, y)
plot_all_curves(x, y)
for degree in range(1, 15):
    plot_curve_with_metrics(x, y, degree)

print_poly_cobf_formula(x, y, 6)
print_poly_cobf_formula(x, y, 7)

plot_metric(x, y, "MAE")
plot_metric(x, y, "RMSE")
plot_metric(x, y, "R squared")
plot_metric(x, y, "Adjusted R squared")

plot_all_metrics(x, y)


import numpy as np

def dataGenerator(seed, n):
    '''
        Randomly generating two data distrbution
    '''

    n1 = n
    mu1 = np.array([2, 3])
    sigma1 = 2.5 * np.matrix([[1, -1], [-1, 3]])

    np.random.seed(seed)
    x1 = np.random.multivariate_normal(mu1, sigma1, n1).T
    y1 = np.ones([1, n1], dtype=np.int64)

    n2 = n
    mu2 = np.array([6, 6])
    sigma2 = 2.5 * np.matrix([[1, -1], [-1, 3]])
    np.random.seed(seed + 100)
    x2 = np.random.multivariate_normal(mu2, sigma2, n2).T
    y2 = -1 * np.ones([1, n2], dtype=np.int64)

    x = np.concatenate((x1, x2), axis=1)
    y = np.concatenate((y1, y2), axis=1)

    return x, y

Quizzes/Computer Quiz 2/Question 1/utils.py


import numpy as np

# This function maps an index in the range [0, count*(count - 1) // 2]
# to a tuple of integers in the range [0,count). The mapping will cover
# all pairs if you use all indices between [0, count*(count - 1) // 2].
def pair_to_index(p1, p2):
    if (p1 < p2): p1, p2 = p2, p1
    return (p1 * (p1 - 1) // 2) + p2

# This function maps an index in the range [0, count*(count - 1) // 2]
# to a tuple of integers in the range [0,count). The mapping will cover
# all pairs if you use all indices between [0, count*(count - 1) // 2].
def index_to_pair(index):
    val = int(((1/4 + 2*index)**(1/2) + 1/2))
    remainder = index - val*(val - 1)//2
    return (val, remainder)

# Compute the number of original elements when given the number of pairs.
# This is done by realizing the following:
#
#      n(n-1) / 2 = f(n)
#   (n^2 - n) / 2 = f(n)
#         n^2 - n = 2 f(n)
#   n^2 - n + 1/4 = 2 f(n) + 1/4
#     (n - 1/2)^2 = 2 f(n) + 1/4
#         n - 1/2 = (2 f(n) + 1/4)^{1/2}
#               n = (2 f(n) + 1/4)^{1/2} + 1/2
#              2n = (8 f(n) + 1)^{1/2} + 1
#               n = ((8 f(n) + 1)^{1/2} + 1) / 2
#
def num_from_pairs(num_pairs):
    return int(round( ((8*num_pairs + 1)**(1/2) + 1) / 2 ))

# Define a custom error for passing arrays of the wrong shape to pairwise.
class ShapeError(Exception): pass

# Compute the distance between all pairs of elements in a single list,
# or the distance of all combined pairs between two lists.
def pairwise_distance(x1, x2=None):
    # Define an error for having the wrong array shape.
    if (len(x1.shape) != 2): raise(ShapeError("Only 2D NumPy arrays are allowed."))
    # Determine use case.
    if (type(x2) == type(None)):
        # Compute the pairwise distances.
        x1_sq = np.sum(x1**2, axis=1, keepdims=True)
        d = (x1_sq + x1_sq[:,0]) - 2 * np.matmul(x1, x1.T)
        # Protect against the errors thta will occur along the diagonal.
        d[np.diag_indices(len(d))] = 1.0
        d[:,:] = np.sqrt(d[:,:])
        d[np.diag_indices(len(d))] = 0.0
        return d
    else:
        if (len(x2.shape) != 2): raise(ShapeError("Only 2D NumPy arrays are allowed."))
        # Compute the pairwise distance between memebers of each set.
        x1_sq = np.sum(x1**2, axis=1, keepdims=True)
        x2_sq = np.sum(x2**2, axis=1)
        return np.sqrt(x1_sq + x2_sq - 2 * np.matmul(x1, x2.T))

# ============================================================
#                          Test Cases
# ============================================================

def _test_pairwise_distance(display=False):
    x = np.random.random((100,30))
    y = np.random.random((10,30))

    # Verify that the pairwise distance (between points in two sets) works.
    dists = pairwise_distance(x, y)
    for i in range(len(x)):
        for j in range(len(y)):
            truth = np.linalg.norm(x[i] - y[j])
            assert(abs(dists[i,j] - truth) < 2**(-32))

    # Verify that the pairwise distance (between points in one set) works.
    dists = pairwise_distance(x)
    for i in range(len(x)):
        for j in range(i+1, len(x)):
            ij = dists[i,j]
            ji = dists[j,i]
            truth = np.linalg.norm(x[i] - x[j])
            assert(ij == ji)
            assert(abs(ij - truth) < 2**(-32))

    if display:
        from scipy.spatial.distance import pdist, cdist
        from util.system import Timer

        x = np.random.random((500,4000))
        y = np.random.random((5000,4000))

        t = Timer()
        d = pdist(x)
        t.stop()
        print("scipy  ", d.shape, t.total)

        t = Timer()
        d = pairwise_distance(x)
        t.stop()
        print("mine   ", d.shape, t.total)

        t = Timer()
        x2 = np.sum(x**2, axis=1, keepdims=True)
        d = np.sqrt((x2 + x2[:,0]) - 2 * np.matmul(x, x.T))
        t.stop()
        print("numpy  ", d.shape, t.total)

        print()

        t = Timer()
        d = cdist(x,y)
        t.stop()
        print("scipy  ", d.shape, t.total)

        t = Timer()
        d = pairwise_distance(x, y)
        t.stop()
        print("mine   ", d.shape, t.total)

        t = Timer()
        x2 = np.sum(x**2, axis=1, keepdims=True)
        y2 = np.sum(y**2, axis=1)
        p = 2 * np.matmul(x, y.T)
        d = np.sqrt(x2 + y2 - p)
        t.stop()
        print("numpy  ", d.shape, t.total)

if __name__ == "__main__":
    _test_pairwise_distance()


import numpy as np
import math
import random

import matplotlib
matplotlib.use('Agg')

import matplotlib.pyplot as plt
from sklearn.cluster import KMeans

def kmeans(x,K):
    np.random.seed(1)
    lenx = np.shape(x)[0]
    kmeans = KMeans(n_clusters = 4, init = 'random').fit(x)

    if K >= 1:
        clusters = range(0, K)
    else:
        print('K debe ser mayor a cero (0)')

    randomsample = np.random.choice(clusters, lenx, replace = True)
    x = np.concatenate((x, randomsample.reshape(lenx,1)), axis = 1)
    leny = np.shape(x)[1]
    poscentroid = np.zeros((K, leny - 1), dtype = float)
    iteracion = 0
    cambios = 1

    while cambios > 0:

        cambios = 0

        for i in range(0, K):
            sumtemp = 0
            cont = 0
            for j in range(0, lenx):
                if x[j, leny - 1] == i:
                    cont = cont + 1
                    temp = x[j, 0:leny - 1]
                    sumtemp = sumtemp + temp
            if cont != 0:
                poscentroid[i, ] = sumtemp/cont

        for i in range(0, lenx):
            distanciastemp = np.zeros((K, 1), dtype = int)
            coord = x[i, 0:leny - 1]
            mindist = 0
            cluster = 0
            for j in range(0, K):
                temp = poscentroid[j, ]
                eucliddist = math.sqrt(np.sum(np.power((temp - coord), 2)))
                if j == 0:
                    mindist = eucliddist
                    cluster = j
                else:
                    if mindist > eucliddist:
                        mindist = eucliddist
                        cluster = j
                distanciastemp[j] = eucliddist
            if x[i, leny - 1] != cluster:
                x[i, leny - 1] = cluster
                cambios = cambios + 1

        iteracion = iteracion + 1

##        xpos = x[:,0]
##        ypos = x[:,1]
##        color = x[:,2]
##
##        centrx = poscentroid[:,0]
##        centry = poscentroid[:,1]
##
##        plt.scatter(xpos,ypos,c=color)
##        plt.scatter(centrx,centry,c='cyan',marker='^')
##        plt.show()

##        print(x)
##        print(iteracion)
##        print(poscentroid)

##    print(kmeans.cluster_centers_)
##    print(poscentroid)
    poskmx = kmeans.cluster_centers_[:,0]
    poskmy = kmeans.cluster_centers_[:,1]

    xpos = x[:,0]
    ypos = x[:,1]
    color = x[:,2]

    centrx = poscentroid[:,0]
    centry = poscentroid[:,1]

    plt.figure()
    plt.scatter(xpos,ypos,c=color)
    plt.scatter(centrx,centry,c='cyan',marker='^')
    plt.scatter(poskmx,poskmy,c='magenta',marker='x')
    plt.savefig("RepresentacionGrafica")

    return poscentroid


import numpy as np
import math as m
import time
import matplotlib.pyplot as plt
import unittest

"""QUESTION 3"""
"""Resolution of the linear system Ax=b by the conjugate gradient method where A is a symmetric positive definite matrix and b a vector (without preconditioning)"""

def conjgrad(A,b,x) :
    r=b-np.dot(A,x)
    p=r
    rsOld= float(np.dot(np.transpose(r),r))
    tab_x=[]
    iter=[]
    for i in range(1,100001):
        Ap=np.dot(A,p)
        n = float(np.dot(np.transpose(p),Ap))
        alpha=rsOld/n
        x=x+ alpha*p
        r=r- alpha*Ap
        rsNew=float(np.dot(np.transpose(r),r))
        tab_x+=[x]
        iter+=[i]
        if m.sqrt(rsNew) < 1e-10 :
            max_iteration=i
            break
        p=r+rsNew/rsOld*p
        rsOld=rsNew
    #print('number of iteration needed to find X',max_iteration)
    return x#x is the solution
                        #tab_x is a list regrouping the values of x at each iteration
                        #iter is a list regrouping the iterations before arriving at the final solution.
"""QUESTION 4"""

def somme(T, i, j):
    return sum(T[i][k] * T[j][k] for k in range(j))

def facto_dense_inc(A):
    (n, n1) = A.shape
    T = np.zeros((n,n))
    for i in range(n):
        for j in range(i + 1):
            if A[i][j] != 0:
                if i==j:
                    T[i][j] = m.sqrt((A[i][i] - somme(T, i, j)))
                else:
                    T[i][j] = (A[i][j] - somme(T, i, j)) / T[j][j]
    return T

def preconditioner(A):
    T= facto_dense_inc(A)
    return np.dot(T,np.transpose(T))

def PreconditionedConjgrad(A,b,x):
    r=b-np.dot(A,x)
    M=preconditioner(A)
    z=np.dot(np.linalg.inv(M),r)
    p=z
    for k in range(1,100001):
        alpha=(np.dot(np.transpose(r),z))/(np.dot(np.transpose(p),np.dot(A,p)))
        x=x+ alpha*p
        r2=r-alpha*(np.dot(A,p))
        rsNew=float(np.dot(np.transpose(r2),r2))
        if m.sqrt(rsNew) < 1e-10 :
            max_iteration=k
            break
        z2=np.dot(np.linalg.inv(M),r2)
        b=(np.dot(np.transpose(z2),r2))/(np.dot(np.transpose(z),r))
        z=z2
        r=r2
    print('number of iteration needed to find X',max_iteration)
    return x

##****************TEST: Conjugate gradient and Preconditionned Conjugate gradient methods
class Test_gradient(unittest.TestCase):
    def test_conjgrad(self):
        A= np.array([[4,1],[1,3]])
        b= np.array([1,2])
        x= np.array([2,1])
        expected= np.array([0.0909,0.6363])
        result= conjgrad(A,b,x)
        for i in range(len(A)):
                self.assertAlmostEqual(result[i],expected[i],3)
    def test_conjgrad_precond(self):
        A= np.array([[4,1],[1,3]])
        b= np.array([1,2])
        x= np.array([2,1])
        expected= np.array([0.0909,0.6363])
        result= PreconditionedConjgrad(A,b,x)
        print()
        for i in range(len(A)):
            self.assertAlmostEqual(result[i],expected[i],3)
'''
def measure_execution_time():
        A= np.array([[4,1],[1,3]])
        b= np.array([1,2])
        x= np.array([2,1])
        init_time1= time.time()
        PreconditionedConjgrad(A,b,x)
        final_time1= time.time()
        init_time2= time.time()
        conjgrad(A,b,x)
        final_time2= time.time()
        print('time needed for non precondionned method to find X',final_time1-init_time1)
        print('time needed for precondionned method to find X',final_time2-init_time2)
measure_execution_time()'''
#s1=TestConjgrad_1(100)
##We generate a curve representing the variations of the relative error according to the number of iterations
##relative error = the difference in magnitudes between the expected solution and the solution found by the algorithm

#------------->inutile de tracer les tests suffisent (mais a vous de voir)

#plt.plot(s1[1],s1[0])
#plt.xlabel('Number of iterations')
#plt.ylabel('Relative error')
#plt.title('Variations of the relative error in the resolution of Ax=b')
#plt.show()

if __name__ == '__main__':
    unittest.main(Test_gradient(), verbosity = 2)