try:
    from scipy.special import beta as _beta, digamma as _digamma
    from scipy.integrate import quad as _quad
    import numpy as _np
    from math import sqrt as _sqrt, log as _log
    from typing import Union, Tuple, Dict, List
    from univariate._base import Infinite
except Exception as e:
    print(f"some modules are missing {e}")

class T(Infinite):
    """
    This class contains implementation of the Student's Distribution for calculating the
    probablity density function and cumulative distribution function. Additionally,
    a t-table __generator is also provided by p-value method. Note that the implementation
    of T(Student's) distribution is defined by beta-functions [#]_.

    .. math::
        \\text{T}(x;\\nu) = \\frac{1}{\\sqrt{\\nu}\\text{B}\\Big(\\frac{1}{2}, \\frac{\\nu}{2}\\Big)} \\Big(1 + \\frac{t^2}{\\nu}\\Big) ^{-\\frac{\\nu+1}{2}}

    .. math::
        \\text{T}(x;\\nu) = \\frac{\\Gamma\\Big( \\frac{\\nu+1}{2} \\Big)}{\\sqrt{\\nu\\pi} \\Gamma{\\Big( \\frac{\\nu}{2} \\Big)}} \\Big(1 + \\frac{x^2}{\\nu}\\Big) ^{- \\frac{v+1}{2}} \\\\

    Args:
        df(int): degrees of freedom (:math:`\\nu`) where df > 0
        x(float): random variable

    References:

        .. [#] Kruschke JK (2015). Doing Bayesian Data Analysis (2nd ed.). Academic Press. ISBN 9780124058880. OCLC 959632184.
        .. [#] Weisstein, Eric W. "Student's t-Distribution." From MathWorld--A Wolfram Web Resource. https://mathworld.wolfram.com/Studentst-Distribution.html

    """

    def __init__(self, df: int, randvar: float):
        if type(df) is not int:
            raise TypeError('degrees of freedom(df) should be a whole number.')
        if df < 0:
            raise ValueError('df parameter must not be less than 0')

        self.df = df

    def pdf(self, x: Union[List[float], _np.ndarray, float]) -> Union[float, _np.ndarray]:
        """
        Args:
            x (Union[List[float], _np.ndarray, float]): random variable(s)

        Returns:
            Union[float, _np.ndarray]: evaluation of pdf at x
        """
        df = self.df

        if isinstance(x, (_np.ndarray, List)):
            if not type(x) is _np.ndarray:
                x = _np.array(x)
            return (1 / (_sqrt(df) * _beta(0.5, df / 2))) * _np.power((1 + _np.power(x, 2) / df), -(df + 1) / 2)

        return (1 / (_sqrt(df) * _beta(0.5, df / 2))) * pow((1 + pow(x, 2) / df), -(df + 1) / 2)

    def cdf(self, x: Union[List[float], _np.ndarray, float]) -> Union[float, _np.ndarray]:
        """
        Args:
            x (Union[List[float], _numpyndarray, float]): data point(s) of interest

        Returns:
            Union[float, numpy.ndarray]: evaluation of cdf at x
        """
        df = self.df
        # Test this for possible performance penalty. See if there is better way to do this.

        def pdf(x, df): return (1 / (_sqrt(df) * _beta(0.5, df / 2))) * \
            pow(1 + pow(x, 2) / df, -(df + 1) / 2)

        def d_pdf(x, df): return _quad(pdf, -_np.inf, x, args=df)[0]

        if isinstance(x, (_np.ndarray, List)):
            if not type(x) is _np.ndarray:
                x = _np.array(x)
            return _np.vectorize(d_pdf)(x, df)

        return d_pdf(x, df)

    def mean(self) -> Union[float, str]:
        """
        Mean of the T-distribution.
        Returns:
            0 for df > 1, otherwise undefined.
        """
        df = self.df
        if df > 1:
            return 0.0
        return "undefined"

    def median(self) -> float:
        """
        Returns: Median of the T-distribution
        """
        return 0.0

    def mode(self) -> float:
        """
        Returns: Mode of the T-distribution
        """
        return 0.0

    def var(self) -> Union[float, str]:
        """
        Returns: Variance of the T-distribution
        """
        df = self.df
        if df > 2:
            return df / (df - 2)
        if df > 1 and df <= 2:
            return _np.inf
        return "undefined"

    def std(self) -> Union[float, str]:
        """
        Returns: Standard Deviation of the T-distribution
        """
        var = self.var()
        if type(var) is float:
            return _sqrt(var)
        return "undefined"

    def skewness(self) -> Union[float, str]:
        """
        Returns: Skewness of the T-distribution
        """
        df = self.df
        if df > 3:
            return 0.0
        return "undefined"

    def kurtosis(self) -> Union[float, str]:
        """
        Returns: Kurtosis of the T-distribution
        """
        df = self.df
        if df > 4:
            return 6 / (df - 4)
        if df > 2 and df <= 4:
            return _np.inf
        return "undefined"

    def entropy(self) -> float:
        """
        Returns: differential entropy of T-distribution

        Reference: Park, S.Y. & Bera, A.K.(2009). Maximum entropy autoregressive conditional heteroskedasticity model. Elsivier.
        link: http://wise.xmu.edu.cn/uploadfiles/paper-masterdownload/2009519932327055475115776.pdf
        """
        df = self.df
        return (df+1)/2 * (_digamma((df+1)/2)-_digamma(df/2)) + _log(_sqrt(df)*_beta(df/2, 1/2))

    def summary(self) -> Dict[str, Union[float, str]]:
        """
        Returns:
            Dictionary of T distirbution moments. This includes standard deviation.
        """
        return {
            'mean': self.mean(), 'median': self.median(), 'mode': self.mode(),
            'var': self.var(), 'std': self.std(), 'skewness': self.skewness(), 'kurtosis': self.kurtosis()
        }


#Import the Libraries
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression

# Let's create a data frame and have a look at it
X=np.linspace(0,1,100)
Y=3*X+2
Y=Y+np.random.normal(0,1,Y.shape)
X = X.reshape(-1, 1)
plt.scatter(X,Y)
plt.xlabel('X',fontsize=15)
plt.ylabel('Y',fontsize=15)
plt.title('Relationship b/w X and Y',fontsize=17)

#let's select the number of bootstraps we want
boot=500
#Create a bootstrap
df=pd.DataFrame(X,columns=['X'])
df['Y']=Y

#Iteration Begins
X = X.reshape(-1, 1)
betas=[]
preds=[]
for i in range(boot):
    df1=df.sample(len(df),replace=True) #random sample with replacement, frac=1 mean size will be the size of data set
    lr=LinearRegression()
    model=lr.fit(df1[['X']],df1['Y'])
    y_pred=model.predict(X)
    plt.plot(X,y_pred,color='red',alpha=0.1)
    betas.append(model.coef_[0])
    preds.append(y_pred)
plt.scatter(X,Y,label='Original')
plt.legend()
plt.title('Bootstraping Estiamtions',fontsize=15,color='green')

#How about wathching the distributions
fig,ax=plt.subplots(figsize=(5,5))
plt.hist(betas,label='Beta Frequency')
plt.xlabel('Beta Values',fontsize=10)
plt.ylabel('Frequencies',fontsize=10)
plt.title('Beta Distribution',fontsize=12,color='red')
plt.axvline(np.array(betas).mean(axis=0),color='red',label='Mean')
plt.legend()
plt.tight_layout()

#Calculating 95% Confidence Intervals for beta1
CI=(np.percentile(betas,2.5),np.percentile(betas,97.25))
print(CI)

## Distribution with CI Visualization
fig,ax=plt.subplots(figsize=(6,6))
plt.hist(betas,label='Beta Frequency',alpha=0.5)
plt.xlabel('Beta Values',fontsize=15)
plt.ylabel('Frequencies',fontsize=15)
plt.title('Beta Distribution',fontsize=17,color='red')
plt.axvline(np.array(betas).mean(axis=0),color='red',label='Mean')
plt.axvline(CI[0],color='green',label='95% Confidence Interval')
plt.axvline(CI[1],color='green',label='95% Confidence Interval')
plt.legend(fontsize='small')
plt.tight_layout()

#95% Confidence Interval of precdiction
fig,ax=plt.subplots(figsize=(6,6))
mean=np.array(preds).mean(axis=0)
plt.plot(X,mean,label='Mean at each bootstrap',color='blue')
plt.xlabel('Y-values',fontsize=15)
plt.ylabel('X-values',fontsize=15)
plt.title('95% Confidence Interval of Predictions',fontsize=17,color='red')
plt.fill_between(X.flatten(),mean-(2*np.std(np.array(preds),axis=0)),mean+(2*np.std(np.array(preds),axis=0)),alpha=0.2)
plt.legend(fontsize='small')
plt.scatter(X,Y)
plt.tight_layout()


from audioop import cross
from math import dist
import numpy as np
import pdb

class KNN(object):

  def __init__(self):
    pass

  def train(self, X, y):
    """
    Inputs:
    - X is a numpy array of size (num_examples, D)
    - y is a numpy array of size (num_examples, )
    """
    self.X_train = X
    self.y_train = y

  def compute_distances(self, X, norm=None):
    """
    Compute the distance between each test point in X and each training point
    in self.X_train.

    Inputs:
    - X: A numpy array of shape (num_test, D) containing test data.
    - norm: the function with which the norm is taken.

    Returns:
    - dists: A numpy array of shape (num_test, num_train) where dists[i, j]
      is the Euclidean distance between the ith test point and the jth training
      point.
    """
    if norm is None:
      norm = lambda x: np.sqrt(np.sum(x**2))
      #norm = 2

    num_test = X.shape[0]
    num_train = self.X_train.shape[0]
    dists = np.zeros((num_test, num_train))
    for i in np.arange(num_test):

      for j in np.arange(num_train):
        # ================================================================ #
        # YOUR CODE HERE:
        #   Compute the distance between the ith test point and the jth
        #   training point using norm(), and store the result in dists[i, j].
        # ================================================================ #

        dists[i,j] = norm(X[i,:]-self.X_train[j,:])

        # ================================================================ #
        # END YOUR CODE HERE
        # ================================================================ #

    return dists

  def compute_L2_distances_vectorized(self, X):
    """
    Compute the distance between each test point in X and each training point
    in self.X_train WITHOUT using any for loops.

    Inputs:
    - X: A numpy array of shape (num_test, D) containing test data.

    Returns:
    - dists: A numpy array of shape (num_test, num_train) where dists[i, j]
      is the Euclidean distance between the ith test point and the jth training
      point.
    """
    num_test = X.shape[0]
    num_train = self.X_train.shape[0]
    dists = np.zeros((num_test, num_train))

    # ================================================================ #
    # YOUR CODE HERE:
    #   Compute the L2 distance between the ith test point and the jth
    #   training point and store the result in dists[i, j].  You may
    #   NOT use a for loop (or list comprehension).  You may only use
    #   numpy operations.
    #
    #   HINT: use broadcasting.  If you have a shape (N,1) array and
    #   a shape (M,) array, adding them together produces a shape (N, M)
    #   array.
    # ================================================================ #
    cross_terms = X@self.X_train.T
    xTx = np.sum(np.square(X),axis=1)
    trTtr = np.sum(np.square(self.X_train),axis=1)
    dists = np.sqrt(-2*cross_terms +trTtr + xTx.reshape(xTx.shape[0],1))
    # ================================================================ #
    # END YOUR CODE HERE
    # ================================================================ #

    return dists

  def predict_labels(self, dists, k=1):
    """
    Given a matrix of distances between test points and training points,
    predict a label for each test point.

    Inputs:
    - dists: A numpy array of shape (num_test, num_train) where dists[i, j]
      gives the distance betwen the ith test point and the jth training point.

    Returns:
    - y: A numpy array of shape (num_test,) containing predicted labels for the
      test data, where y[i] is the predicted label for the test point X[i].
    """
    num_test = dists.shape[0]
    y_pred = np.zeros(num_test)
    for i in np.arange(num_test):
      # A list of length k storing the labels of the k nearest neighbors to
      # the ith test point.
      closest_y = []
      # ================================================================ #
      # YOUR CODE HERE:
      #   Use the distances to calculate and then store the labels of
      #   the k-nearest neighbors to the ith test point.  The function
      #   numpy.argsort may be useful.
      #
      #   After doing this, find the most common label of the k-nearest
      #   neighbors.  Store the predicted label of the ith training example
      #   as y_pred[i].  Break ties by choosing the smaller label.
      # ================================================================ #
      ind = np.argsort(dists[i])
      ind = ind[0:k]
      closest_y = self.y_train[ind]
      y_pred[i] = np.bincount(closest_y).argmax() # will return first occurance bc sorted bins
      # ================================================================ #
      # END YOUR CODE HERE
      # ================================================================ #

    return y_pred


import numpy as np
from scipy.integrate import quad
from math import sin, cos, pi, exp

def Find_Heaviside_Wavelet_Two(T0,amp,Resolut):
    """
    This function computes the wavelet transform of a heaviside function
    input:
        T0: float, representing the time where the step happen
        amp: float, representing the amplitude of the step
        Resolut: int, representing the resolution of the result
    return:
        a: array_like of size (1xResolut), discretization of the y-axis
        b: array_like of size (1xResolut), discretization of the x-axis
        W: array_like of size (ResolutxResolut), matrix containing the wavelet transform value at each (a,b)
    """
    a = np.linspace(0.1, 5, Resolut)
    b = np.linspace(0.1, 5, Resolut)
    W = np.zeros((Resolut, Resolut))

    for i in range (0,Resolut):
        for j in range (0,Resolut):
            def integrand1(t):
                output = a[i]**-0.5 * amp * exp(-(((t-b[j])/a[i])**2)/2) * cos(5*((t-b[j])/a[i]))
                return output
            W[i][j]=quad(integrand1, T0, 15)[0]

    return a,b,W

def Find_Rectangular_Wavelet_Two(T0,T1,amp,Resolut):
    """
    This function computes the wavelet transform of a rectangular function
    input:
        T0: float, representing the time where the first step happen
        T1: float, representing the time where the second step happen
        amp: float, representing the amplitude of the step
        Resolut: int, representing the resolution of the result
    return:
        a: array_like of size (1xResolut), discretization of the y-axis
        b: array_like of size (1xResolut), discretization of the x-axis
        W: array_like of size (ResolutxResolut), matrix containing the wavelet transform value at each (a,b)
    """
    a=np.linspace(0.1,5,Resolut)
    b=np.linspace(0.1,5,Resolut)
    W = np.zeros((Resolut, Resolut))

    for i in range (0,Resolut):
        for j in range (0,Resolut):
            def integrand1(t):
                output = a[i]**-0.5 * amp * exp(-(((t-b[j])/a[i])**2)/2) * cos(5*((t-b[j])/a[i]))
                return output
            W[i][j]=quad(integrand1, T0, T1)[0]

    return a,b,W

def Find_Dirac_Wavelet_Two(T0, amp,Resolut):
    """
    This function computes the wavelet transform of a dirac function
    input:
        T0: float, representing the impulse happen
        amp: float, representing the amplitude of the impulse
        Resolut: int, representing the resolution of the result
    return:
        a: array_like of size (1xResolut), discretization of the y-axis
        b: array_like of size (1xResolut), discretization of the x-axis
        W: array_like of size (ResolutxResolut), matrix containing the wavelet transform value at each (a,b)
    """
    a=np.linspace(0.1,5,Resolut)
    b=np.linspace(0.1,5,Resolut)
    W = np.zeros((Resolut, Resolut))

    for i in range (0,Resolut):
        for j in range (0,Resolut):
            W[i][j]= a[i]**-0.5 * amp * exp(-(((T0-b[j])/a[i])**2)/2) * cos(5*((T0-b[j])/a[i]))

    return a,b,W

def Find_Trig_Wavelet_Two(index, frequency, Resolut):
    """
    This function computes the wavelet transform of sin and cos functions
    input:
        index: int, defining which trigonometric function to be used {0 -> sin, 1 -> cos}
        frequency: float, representing the the frequency of the trigonometric function
        Resolut: int, representing the resolution of the result
    return:
        a: array_like of size (1xResolut), discretization of the y-axis
        b: array_like of size (1xResolut), discretization of the x-axis
        W: array_like of size (ResolutxResolut), matrix containing the wavelet transform value at each (a,b)
    """
    a=np.linspace(0.1,5,Resolut)
    b=np.linspace(0.1,5,Resolut)
    W=np.zeros((Resolut, Resolut))

    if (index == 0):

        for i in range (0,Resolut):
            for j in range (0,Resolut):
                def integrand1(t):
                    output = a[i]**-0.5 * np.sin(2 * np.pi * frequency * t) * exp(-(((t-b[j])/a[i])**2)/2) * cos(5*((t-b[j])/a[i]))
                    return output
                W[i][j]=quad(integrand1, -10, 15)[0]

    else:
        for i in range (0,Resolut):
            for j in range (0,Resolut):
                def integrand1(t):
                    output = a[i]**-0.5 * np.cos(2 * np.pi * frequency * t) * exp(-(((t-b[j])/a[i])**2)/2) * cos(5*((t-b[j])/a[i]))
                    return output
                W[i][j]=quad(integrand1, -10, 15)[0]

    return a,b,W

def Find_Custom_Wavelet_Two(user_func,Resolut):
    """
    This function computes the wavelet transform of sin and cos functions
    input:
        user_func: string, defining the user defined function
        Resolut: int, representing the resolution of the result
    return:
        a: array_like of size (1xResolut), discretization of the y-axis
        b: array_like of size (1xResolut), discretization of the x-axis
        W: array_like of size (ResolutxResolut), matrix containing the wavelet transform value at each (a,b)
    """
    a=np.linspace(0.1,5,Resolut)
    b=np.linspace(0.1,5,Resolut)
    W = np.zeros((Resolut, Resolut))

    #make a list of safe functions
    safe_dict = {
        'sin' : sin,
        'cos' : cos,
        'pi' : pi,
        'exp' : exp,
    }
    # WT= "exp(-(((t-b)/a)**2)/2) * cos(5*((t-b)/a))"
    # Stand_WT= "((t-b)/a) * exp(-((t-b)/a)**2)"
    # eval(Stand_WT, safe_dict)
    for i in range (0,Resolut):
        for j in range (0,Resolut):
            def integrand(t):
                safe_dict['t'] = t
                try:
                    return a[i]**-0.5 * eval(user_func, safe_dict) * exp(-(((t-b[j])/a[i])**2)/2) * cos(5*((t-b[j])/a[i]))
                except NameError:
                    pass
            W[i][j]=quad(integrand, -10, 15)[0]

    return a,b,W


import numpy as np
from scipy.optimize import least_squares

def D(S, V, assignment):
    # From a flattened S to a mapped S (according to assignment)
    mappedS = np.zeros((V.shape[0], V.shape[1]+1))
    vert_num = 0
    for sphere_num in assignment:
        mappedS[vert_num, :] = S[4*sphere_num:4*sphere_num+4]
        vert_num += 1

    return  (np.sqrt( (mappedS[:, 0] - V[:, 0])**2
                    + (mappedS[:, 1] - V[:, 1])**2
                    + (mappedS[:, 2] - V[:, 2])**2
                    ) - mappedS[:, 3]
            ) # using least square optimizer, no abs value needed

def J(S, V, assignment):
    J = np.zeros((V.shape[0], len(S)))

    mappedS = np.zeros((V.shape[0], V.shape[1]+1))
    vert_num = 0
    for sphere_num in assignment:
        mappedS[vert_num, :] = S[4*sphere_num:4*sphere_num+4]
        vert_num += 1

    # func_val = np.sqrt( (mappedS[:, 0] - V[:, 0])**2
    #                 + (mappedS[:, 1] - V[:, 1])**2
    #                 + (mappedS[:, 2] - V[:, 2])**2
    #               ) - mappedS[:, 3]
    # sign = np.empty(V.shape[0])
    # for i in range(len(sign)):
    #    sign[i] = 1.0 if func_val[i] >= 0 else -1.0

    deriv = 0.5 * (
                (mappedS[:, 0] - V[:, 0])**2
                + (mappedS[:, 1] - V[:, 1])**2
                + (mappedS[:, 2] - V[:, 2])**2
            )**(-0.5)

    vert_num = 0
    for sphere_num in assignment:
        J[vert_num, 4*sphere_num] = deriv[vert_num] * 2 * (mappedS[vert_num, 0] - V[vert_num, 0]) # * sign[vert_num]
        J[vert_num, 4*sphere_num+1] = deriv[vert_num] * 2 * (mappedS[vert_num, 1] - V[vert_num, 1]) # * sign[vert_num]
        J[vert_num, 4*sphere_num+2] = deriv[vert_num] * 2 * (mappedS[vert_num, 2] - V[vert_num, 2]) # * sign[vert_num]
        J[vert_num, 4*sphere_num+3] = -1.0 # * sign[vert_num]
        vert_num += 1

    return J

def optimizeAsgdSpheresFromVert(verts, spheres, assignment):
    S = spheres.flatten()
    V = np.empty((len(verts), 3))
    for i in range(len(verts)):
        V[i, :] = np.array(verts[i])

    optRes = least_squares(D, S, jac=J, args=(V, assignment), verbose=1)
    optS = np.reshape(optRes.x, (-1, 4))
    return optS


import numpy as np

def f(x):

     s=x**2+x+1

     return s

def F(x):

   s=x**3/3+x**2/2+x

   return s

x=[0,1]

quad=(1/2)*(f(x[0])+f(x[1]))

print('approximation with Trapezontial rule :', quad)
print('real solution of integration : ', F(1)-F(0))


import numpy as np
from scipy.sparse import csr_matrix, identity

def grad1D(k, m, dx):
    """Computes a m+1 by m+2 one-dimensional mimetic gradient operator

    Arguments:
        k (int): Order of accuracy
        m (int): Number of cells
        dx (float): Step size

    Returns:
        :obj:`ndarray` containing discrete gradient operator
    """

    assert k >= 2, "Wrong order of accuracy: {}".format(k)
    assert k % 2 == 0, "Order of accuracy must be an even number: {}".format(k)
    assert m >= 2 * k, "m must be >= {} for k = {}".format(2 * k, k)

    """
    Dimensions of G
    """
    n_rows = m + 1
    n_cols = m + 2

    G = csr_matrix((n_rows, n_cols), dtype=np.float)

    """
    Fill the middle of G
    """
    neighbors = np.arange(0.5 - k/2., k/2. + 0.5)

    """
    Create a k by k Vandermonde matrix based on the neighbors
    """
    A = np.transpose(np.vander(neighbors))

    """
    First-order derivative
    """
    b = np.zeros((k, 1), dtype=np.float)
    b[k-2, None] = 1.

    """
    Solve the linear system to get the coefficients
    """
    coeffs = np.transpose(np.linalg.solve(A, b))

    j = 1
    for i in range(int(k/2), int(n_rows - k/2)):
        G[i, j:j+k] = coeffs
        j = j + 1

    """
    Create A
    """
    p = int(k / 2)
    q = int(k + 1)
    A = csr_matrix((p, q), dtype=np.float)
    # For each row of A
    for i in range(p):
        """
        k+1 points are used for the boundaries
        Shifting the stencil to the right
        """
        neighbors = np.insert(np.arange(0.5 - i, q - i - 0.5), 0, -i)
        V = np.transpose(np.vander(neighbors))
        b = np.zeros((q, 1), dtype=np.float)
        b[q-2, None] = 1.
        coeffs = np.transpose(np.linalg.solve(V, b))
        A[i, 0:q] = coeffs

    """
    Insert A into G (upper-left corner of G)
    """
    if A.count_nonzero() != 0:
        G[0:p, 0:q] = A

    """
    Permutation matrices
    """
    Pp = csr_matrix(np.fliplr(identity(p).toarray()), dtype=np.float)
    Pq = csr_matrix(np.fliplr(identity(q).toarray()), dtype=np.float)

    """
    Construct A' (lower-right corner of G)
    """
    A = -Pp * A * Pq

    """
    Insert A' into G
    """
    if A.count_nonzero() != 0:
        G[n_rows-p:n_rows, n_cols-q:n_cols] = A

    """
    Scale G
    """
    return G / dx

if __name__ == '__main__':
    grad1D(2, 4, 1)
    print(grad1D(4, 8, 1))
    grad1D(6, 12, 1)


from __future__ import division
import matplotlib
matplotlib.rcParams = matplotlib.rc_params_from_file('../../matplotlibrc')

import numpy as np
from solution import general_secondorder_ode_fd, poisson_square
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d.axes3d import Axes3D
from matplotlib import cm
import matplotlib.colors as mcolors
from scipy.sparse import spdiags
from scipy.sparse.linalg import spsolve

def example():
    # First Code block in the lab manual
    import numpy as np
    from scipy.sparse import spdiags
    from scipy.sparse.linalg import spsolve

    def bvp(func, a, b, alpha, beta, N):
        # Solving u'' = f(x), x in [a, b],
        # u(a) = alpha, u(b) = beta
        # N = number of subintervals
        #
        # We use the finite difference method to construct a system
        # of algebraic equations described by the matrix equation
        # AU = F

        h = (b-a)/N 	# The length of each subinterval

        # Initialize and define the vector F on the right
        F = np.empty(N-1.)
        F[0] = func(a+1.*h)-alpha*h**(-2.)
        F[N-2] = func(a+(N-1)*h)-beta*h**(-2.)
        for j in xrange(1,N-2):
                F[j] = func(a + (j+1)*h)

        # Here we define the arrays that will go on the diagonals of A
        data = np.empty((3,N-1))
        data[0,:] = -2.*np.ones((1,N-1)) # main diagonal
        data[1,:], data[2,:] = np.ones((1,N-1)), np.ones((1,N-1))  # off-diagonals
        # Next we specify on which diagonals they will be placed
        diags = np.array([0,-1,1])

        A=h**(-2.)*spdiags(data,diags,N-1,N-1).asformat('csr')

        # We create and return the numerical approximation
        U = np.empty(N+1)
        U[1:-1] = spsolve(A,F)
        U[0], U[-1] = alpha, beta
        return np.linspace(a,b,N+1), U

    x, y = bvp(lambda x:(-3.*np.sin(x)), a=0., b=2., alpha=-2., beta=1, N=30)
    import matplotlib.pyplot as plt
    plt.plot(x,y,'-k',linewidth=2.0)
    plt.show()

def Exercise1():
    def problem1(epsilon, subintervals):
        X,Y = general_secondorder_ode_fd(func=lambda x:-1.,a1=lambda x:epsilon,
                                        a2=lambda x: -1.,a3=lambda x:0.,
                                        a=0.,b=1., alpha=1.,beta=3.,N=subintervals)
        return X,Y

    def AnalyticSolution(x,alpha, beta,epsilon):
        out = alpha+x+(beta-alpha-1.)*(np.exp(x/epsilon) -1.)/(np.exp(1./epsilon) -1.)
        return out

    eps, subintervals = 0.1, 400
    X,Y = problem1(eps, subintervals)
    plt.plot(X,Y,'-k',mfc="None",linewidth=2.0)
    plt.axis([0.,1.1,.8,3.2])
    plt.ylabel('$y$',fontsize=16)
    plt.xlabel('$x$',fontsize=16)
    plt.savefig('figure2.pdf')
    plt.show()
    plt.clf()

    N = np.array([5,10,20,40,80,160,320,640,1280,2560])
    h, MaxError = 2./N, np.ones(len(N))
    eps = .1
    for j in range(len(N)):
        Mesh, Sol = problem1(epsilon=eps,subintervals=N[j])
        MaxError[j] = (max(abs( Sol- AnalyticSolution(Mesh,alpha=1.,beta=3.,epsilon=eps) ) ) )
    print "Number of subintervals = ", N
    print "MaxError = ",MaxError
    plt.loglog(h,h**(2.),'-k',label="$h^{\, 2}$" )
    plt.loglog(h,MaxError,'.-r',label="$E(h^2)$")
    plt.xlabel("$h$")
    plt.legend(loc='best')
    # plt.show()
    print "Order of the Approximation is about ", ( (np.log(MaxError[0]) -
            np.log(MaxError[-1]) )/( np.log(h[0]) - np.log(h[-1]) ) )

def ExercisePoisson():
    from numpy import sin, cos, pi
    # Domain: [0,1]x[0,1]
    a1,b1 = 0.,1.
    c1,d1 = 0.,1.
    n=100
    # Example1: Laplace's equation (Poisson with no source)
    def bcs(x,y):
        return x**3.

    def source(x,y):
        return 0.

    # # Example2: Poisson's equation
    # def bcs(x,y): return sin(pi*x)*cos(2.*pi*y)
    # def source(x,y): return -5.*(pi**2.)*bcs(x,y)

    # # Example3: Poisson's equation
    # def bcs(x,y): return sin(2.*pi*y)*cos(pi*x)
    # def source(x,y): return -5.*(pi**2.)*bcs(x,y)

    # # Example4: Poisson's equation
    # def bcs(x,y): return 1.-x +x*y + (1./2)*sin(pi*x)*sin(pi*y)
    #
    # def source(x,y): return -(pi**2)*sin(pi*x)*sin(pi*y)

    z=poisson_square(a1,b1,c1,d1,n,bcs,source)
    print '---------------'
    print "Computation successful"
    print '---------------'
    # Plotting data
    fig = plt.figure()
    #---- First subplot: Numerical Solution
    # ax = fig.add_subplot(121, projection='3d')
    ax = fig.gca(projection='3d')
    ax.set_xlabel('X'); ax.set_ylabel('Y'); ax.set_zlabel('Z')
    x, y = np.linspace(a1,b1,n+1), np.linspace(c1,d1,n+1)
    xv, yv = np.meshgrid(x, y)
    xv, yv = xv.T, yv.T
    surf = ax.plot_surface(xv, yv, z, rstride=2, cstride=2, cmap=cm.coolwarm,
            linewidth=0, antialiased=False)
    # #---- Second subplot: Exact Solution
    # ax2 = fig.add_subplot(122, projection='3d')
    # ax2.set_xlabel('X'); ax2.set_ylabel('Y'); ax2.set_zlabel('Z')
    # surf2 = ax2.plot_surface(xv, yv, bcs(xv,yv), rstride=2, cstride=2, cmap=cm.coolwarm,
    # 	        linewidth=0, antialiased=False)
    print "Maximum Error = \n", np.max(np.abs( z-bcs(xv,yv) ) )
    # plt.savefig('./Poisson_solution.png')
    plt.clf()
    # plt.show()

    # if True: return

    num_approx = 7 # Number of Approximations
    N = np.array([10*2**(j) for j in range(num_approx)])
    h, max_error = (b1-a1)/N[:-1], np.ones(num_approx-1)

    num_sol_best = poisson_square(a1,b1,c1,d1,N[-1],bcs,source)
    for j in range(len(N)-1):
        num_sol = poisson_square(a1,b1,c1,d1,N[j],bcs,source)
        max_error[j] = np.max(np.abs( num_sol- num_sol_best[::2**(num_approx-j-1), ::2**(num_approx-j-1)] ) )
    plt.loglog(h,max_error,'.-r',label="$E(h)$")
    plt.loglog(h,h**(2.),'-k',label="$h^{\, 2}$")
    plt.xlabel("$h$")
    plt.legend(loc='best')
    print "The order of the finite difference approximation is about ", ( (np.log(max_error[0]) -
        np.log(max_error[-1]) )/( np.log(h[0]) - np.log(h[-1]) ) ), "."
    plt.savefig('./Poisson_Error.pdf')
    plt.show()

def plotRhos():
    #definitions for atoms position and charges
    #the angle the hydrogen atoms make
    theta = 106.0/180.0*np.pi
    #Length of the two branches
    A = 1.0
    # Hydrogen 1 (x0,y0,q)
    # Hydrogen 2
    # Oxygen
    water = ((-np.sin(theta/2)*A	, 0				, 1),
                ( np.sin(theta/2)*A	, 0				 	, 1),
                ( 0					, -np.cos(theta/2)*A,-2))

    def rho1(x,y,atom):
            return atom[2]*np.exp(-np.sqrt((x-atom[0])**2 + (y-atom[1])**2))

    def rhoSum(x,y,atoms):
            return np.sum([rho1(x,y,atom) for atom in atoms],axis=0)

    #Generate a color dictionary for use with LinearSegmentedColormap
    #that places red and blue at the min and max values of data
    #and white when data is zero
    def genDict(data):
            zero = 1/(1 - np.max(data)/np.min(data))
            cdict = {'red':   [(0.0,  1.0, 1.0),
                        (zero,  1.0, 1.0),
                        (1.0,  0.0, 0.0)],
                'green': [(0.0,  0.0, 0.0),
                        (zero,  1.0, 1.0),
                        (1.0,  0.0, 0.0)],
                'blue':  [(0.0,  0.0, 0.0),
                        (zero,  1.0, 1.0),
                        (1.0,  1.0, 1.0)]}
            return cdict

    m = 500
    X = np.linspace(-5,5,m)
    X,Y = np.meshgrid(X,X)
    #Generate the grid of rho values
    Rho = rhoSum(X,Y,water)
    plt.imshow(Rho,cmap =  mcolors.LinearSegmentedColormap('cmap', genDict(Rho)))
    plt.colorbar(label="Relative Charge Density")
    plt.savefig("./waterRho.pdf")
    plt.clf()

    co2 = ((-1,0,-2),
    (1,0,-2),
    (0,0,4))
    Rho = rhoSum(X,Y,co2)
    plt.imshow(Rho,cmap =  mcolors.LinearSegmentedColormap('cmap', genDict(Rho)))
    plt.colorbar(label="Relative Charge Density")
    plt.savefig("./co2Rho.pdf")
    plt.clf()

def plotVs():

    #definitions for atoms position and charges
    #the angle the hydrogen atoms make
    theta = 106.0/180.0*np.pi
    #Length of the two branches
    A = 1.0
    # Hydrogen 1 (x0,y0,q)
    # Hydrogen 2
    # Oxygen
    water = ((-np.sin(theta/2)*A,0,1),
    (np.sin(theta/2)*A,0,1),
    (0,-np.cos(theta/2)*A,-2))

    def rho1(x,y,atom):
        return atom[2]*np.exp(-np.sqrt((x-atom[0])**2 + (y-atom[1])**2))

    def rhoSum(x,y,atoms):
        return np.sum([rho1(x,y,atom) for atom in atoms],axis=0)

    def poisson_square(a1,b1,c1,d1,n,bcs, source):
        #n = number of subintervals
        # We discretize in the x dimension by a1 = x_0 < x_1< ... < x_n=b1, and
        # we discretize in the y dimension by c1 = y_0 < y_1< ... < y_n=d1.
        # This means that we have interior points {x_1, ..., x_{n-1}}\times {y_1, ..., y_{n-1}}
        # or {x_1, ..., x_m}\times {y_1, ..., y_m} where m = n-1.
        # In Python, this is indexed as {x_0, ..., x_{m-1}}\times {y_0, ..., y_{m-1}}
        # We will have m**2 pairs of interior points, and m**2 corresponding equations.
        # We will organize these equations by their y coordinates: all equations centered
        # at (x_i, y_0) will be listed first, then (x_i, y_1), and so on till (x_i, y_{m-1})
        delta_x, delta_y, h, m = (b1-a1)/n, (d1-c1)/n, (b1-a1)/n, n-1

        ####    Here we construct the matrix A    ####
        ##############################     Slow            #################################
        #     D, diags = np.ones((1,m**2)), np.array([-m,m])
        #     data = np.concatenate((D, D),axis=0)
        #     A = h**(-2)*spdiags(data,diags,m**2,m**2).asformat('lil')
        #     D = np.ones((1,m))
        #     diags, data = np.array([0,-1,1]), np.concatenate((-4.*D,D,D),axis=0)
        #     temp = h**(-2)*spdiags(data,diags,m,m).asformat('lil')
        #     for i in xrange(m): A[i*m:(i+1)*m,i*m:(i+1)*m] = temp

        ##############################     Much Faster      ################################
        D1,D2,D3 = -4*np.ones((1,m**2)), np.ones((1,m**2)), np.ones((1,m**2))
        Dm1, Dm2 = np.ones((1,m**2)), np.ones((1,m**2))
        for j in range(0,D2.shape[1]):
                if (j%m)==m-1: D2[0,j]=0
                if (j%m)==0: D3[0,j]=0
        diags = np.array([0,-1,1,-m,m])
        data = np.concatenate((D1,D2,D3,Dm1,Dm2),axis=0) # This stacks up rows
        A = 1./h**2.*spdiags(data, diags, m**2,m**2).asformat('csr') # This appears to work correctly

        ####    Here we construct the vector b    ####
        b, Array = np.zeros(m**2), np.linspace(0.,1.,m+2)[1:-1]
        # In the next line, source represents the inhomogenous part of Poisson's equation
        for j in xrange(m): b[j*m:(j+1)*m] = source(a1+(b1-a1)*Array, c1+(j+1)*h*np.ones(m) )

        # In the next four lines, bcs represents the Dirichlet conditions on the boundary
#     y = c1+h, d1-h
        b[0:m] = b[0:m] - h**(-2.)*bcs(a1+(b1-a1)*Array,c1*np.ones(m))
        b[(m-1)*m : m**2] = b[(m-1)*m : m**2] - h**(-2.)*bcs(a1+(b1-a1)*Array,d1*np.ones(m))
#     x = a1+h, b1-h
        b[0::m] = b[0::m] - h**(-2.)*bcs(a1*np.ones(m),c1+(d1-c1)*Array)
        b[(m-1)::m] = b[(m-1)::m] - h**(-2.)*bcs(b1*np.ones(m),c1+(d1-c1)*Array)

        ####    Here we solve the system A*soln = b    ####
        soln = spsolve(A,b) # Using the conjugate gradient method: (soln, info) = cg(A,b)

        z = np.zeros((m+2,m+2) )
        for j in xrange(m): z[1:-1,j+1] = soln[j*m:(j+1)*m]

        x, y = np.linspace(a1,b1,m+2), np.linspace(c1,d1,m+2)
        z[:,0], z[:,m+1]  = bcs(x,c1*np.ones(len(x)) ), bcs(x,d1*np.ones(len(x)) )
        z[0,:], z[m+1,:] = bcs(a1*np.ones(len(x)),y), bcs(b1*np.ones(len(x)),y)
        return z

    #Generate a color dictionary for use with LinearSegmentedColormap
    #that places red and blue at the min and max values of data
    #and white when data is zero
    def genDict(data):
        zero = 1/(1 - np.max(data)/np.min(data))
        cdict = {'red':   [(0.0,  1.0, 1.0),
                    (zero,  1.0, 1.0),
                    (1.0,  0.0, 0.0)],
            'green': [(0.0,  0.0, 0.0),
                    (zero,  1.0, 1.0),
                    (1.0,  0.0, 0.0)],
            'blue':  [(0.0,  0.0, 0.0),
                    (zero,  1.0, 1.0),
                    (1.0,  1.0, 1.0)]}
        return cdict

    a1 = -5
    b1 = 5
    m = 500
    X = np.linspace(a1,b1,m)
    X,Y = np.meshgrid(X,X)
    #poisson_square seems to mix up x and y
    Rho = poisson_square(a1,b1,a1,b1,m,lambda x,y:0 , lambda x,y: -rhoSum(y,x,water))
    cdict = genDict(Rho)

    plt.imshow(Rho,cmap = mcolors.LinearSegmentedColormap('CustomMap', cdict))
    plt.colorbar(label="Relative Voltage")
    plt.savefig("./waterV.pdf")
    plt.clf()

    co2 = ((-1,0,-2),
                (1,0,-2),
                (0,0,4))
    Rho = poisson_square(a1,b1,a1,b1,m,lambda x,y:0 , lambda x,y: -rhoSum(y,x,co2))
    cdict = genDict(Rho)
    plt.imshow(Rho,cmap = mcolors.LinearSegmentedColormap('CustomMap', cdict))
    plt.colorbar(label="Relative Voltage")
    plt.savefig("co2V.pdf")
    plt.clf()

if __name__ == "__main__":
    example()
    Exercise1()
    ExercisePoisson()
    plotRhos()
    plotVs()


# -*- coding: utf-8 -*-
"""
Created on Wed Feb  3 16:08:35 2021

@author: LI Zhengyang
"""

"""
Cutting Stock Problem
The length of a raw pipe is 218cm. Now the client wants 44 pipes of 81cm, 3 pipes of 70cm, 48 pipes of 68cm, what is the best cutting plan?

Column generation algorithm:
    Description:
        The column generation will decompose the original problem in to a restricted master problem (RMP) and a subproblem (SP),
        and solve RMP and SP iteratively until the optimal solution is obtained.

    parameters:
        a(ij): the number of pipes i that can obtain from a raw pipe in cutting plan j.
        l(i): the length of pipe i.
        L: the length of a raw pipe.
        b(i): the required number of pipes i.
        pi(i): the dual price of constraint i in the RMP.

    Variables:
        x(j): the number of implement times of cutting plan j.
        y(i): the number of pipes i that can obtain from a raw pipe in the new cutting plan.

    model:
    RMP (output the dual price p(i) to check whether we can find a better cutting plan):
        min = sum_j [ x(j) ]
        s.t.
        sum_j [ a(ij) * x(j) ] >= b(i)       for i in I (Set of pipes' types)
        x(j) are continuous variables

        Note: In order to get p(i), we can also solve the dual of RMP, where p(i) will be the solution vector. Acutally, solving the dual of RMP is a better way since
        the number of variables in RMP dual will not increase. Here, we directly solve the original problem, because this example is quite easy.

    SP (output a better cutting plan y(i), and resolve the RMP):
        max = 1 - sum_i [p(i) * y(i)]
        s.t.
        sum_i [ l(i) * y(i) ] <= L          for j in J (Set of cutting plan)
        y(i) are integer variables

    Stopping criteria:
        The RMP and SP will be iteratively implemented until the objective value of SP is less than 0. That means, no better cutting plan exisits. Dude, let's stop.
"""

import sys
import numpy as np
import gurobipy as gp
from gurobipy import GRB
import time

timeStart = time.time()

class Material:
    def __init__(self, name, length):
        self.name = name
        self.length = length
        self.num = float("inf")

class Demand:
    def __init__(self, name, length, num):
        self.name = name
        self.length = length
        self.num = num

matSet = {}
demSet = {}

matSet[1] = Material('rawPipe', 218)
demSet[1] = Demand('pipe1', 81, 44)
demSet[2] = Demand('pipe2', 70, 3)
demSet[3] = Demand('pipe3', 68, 48)

def init_solution():
    """
    provide a initial cutting plan for the RMP
    """
    c = np.ones(3)
    A = np.identity(3)
    b = []
    for i in demSet:
        b.append(demSet[i].num)
    b = np.array(b)
    return c,A,b

def solver_RMP(c,A,b):
    m = gp.Model("CG")
    x = m.addMVar(len(c), vtype='C', name="x")
    m.setObjective(c @ x, GRB.MINIMIZE)
    m.addConstr(A @ x >= b)
    m.optimize()
    sol = x.X
    return m.objVal, sol, m.Pi

def sp(dualPrice):
    """
    generate the model parameters for sp
    """
    c = np.array(dualPrice)
    A = []
    for i in demSet:
        A.append(demSet[i].length)
    A = np.array(A)
    b = np.array([matSet[1].length])
    return c, A, b

def solver_SP(c,A,b):
    m = gp.Model("CG")
    x = m.addMVar(len(c), vtype='I', name="x")
    m.setObjective(c @ x, GRB.MAXIMIZE)
    m.addConstr(A @ x <= b)
    m.optimize()
    sol = x.X
    return m.objVal, sol

#initialization
spObj = float("inf")
iter = 0
rmpC,rmpA,rmpB = init_solution()

while 1-spObj < 0 and iter < 500:
    iter = iter + 1

    rmpObj, rmpSol, rmpDual = solver_RMP(rmpC,rmpA,rmpB)

    c,A,b = sp(rmpDual)
    spObj, spSol = solver_SP(c,A,b)

    print('           ')
    print('Iter=', iter)
    print('Obj=',rmpObj)
    print('Cutting plan (each column is a plan):')
    print(rmpA)
    print('Number of implementation times for each cutting plan:')
    print(rmpSol)

    """
    Generation a new column in the RMP problem
    """
    rmpC = np.ones(len(demSet)+iter)
    rmpA = np.hstack([ rmpA , np.reshape(np.array(spSol),(len(demSet),1)) ])

timeEnd = time.time()
print('Optimal cutting plan found !')
print('Running time =', timeEnd-timeStart)


import numpy as np
from displayarray import display

def mandel(
    height=240, width=320, itermax=255, y_min=-1.8, y_max=0.6, x_min=-1.6, x_max=1.6
):
    """
    Generate a view of the mandlebrot fractal

    source: https://thesamovar.wordpress.com/2009/03/22/fast-fractals-with-python-and-numpy/

    .. code-block:: python

      >>> img = mandel()
      >>> center = (0, -1.78)
      >>> length = 3.2
      >>> d = display(img)
      >>> while d:
      ...   length*=.9
      ...   y_min = center[1]-length/2.0
      ...   y_max = center[1]+length/2.0
      ...   x_min = center[0]-length/2.0
      ...   x_max = center[0]+length/2.0
      ...   img[...] = mandel(y_min=y_min, y_max=y_max, x_min=x_min, x_max=x_max)
    """

    ix, iy = np.mgrid[0:height, 0:width]
    x = np.linspace(y_min, y_max, height)[ix]
    y = np.linspace(x_min, x_max, width)[iy]
    c = x + complex(0, 1) * y
    del x, y
    img = np.zeros_like(c, dtype=int)
    c.shape = iy.shape = ix.shape = height * width
    z = np.copy(c)
    for i in range(itermax):
        if not len(z):
            break
        z = z * z
        z = z + c
        rem = abs(z) > 4.0
        img[ix[rem], iy[rem]] = i + 1
        rem = ~rem
        z = z[rem]
        ix, iy = ix[rem], iy[rem]
        c = c[rem]
    return img / 255.0

if __name__ == "__main__":
    img = mandel()
    center = (0, -0.6)
    length = 3.2
    d = display(img)
    while d:
        length *= 0.9
        y_min = center[1] - length / 2.0
        y_max = center[1] + length / 2.0
        x_min = center[0] - length / 2.0
        x_max = center[0] + length / 2.0
        img[...] = mandel(y_min=y_min, y_max=y_max, x_min=x_min, x_max=x_max)


'''
Locally weighted linear regression is a non-parametric algorithm, that is,
the model does not learn a fixed set of parameters as is done in ordinary linear regression.
Rather parameters 'theta' are computed individually for each query point x.
While computing 'theta', a higher “preference” is given to the points in the
training set lying in the vicinity of x than the points lying far away from x.

Get the dataset from 'https://www.kaggle.com/aswin1871/tips-csv'
'''

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

def kernel(point,xmat,k):
    '''
        This function calculates the weights kernel which is used to find the local weights.
    '''
    m,n = np.shape(xmat)

    # Stores the weight matrix consisting of 1s along the diagonals and 0s elsewhere.
    weights = np.mat(np.eye(m))

    for j in range(m):
        # Calculates the distance between each point under consideration and all other points of the dataset
        diff = point-xmat[j]

        # Perfoms the kernel weight equation
        weights[j,j] = np.exp((diff*diff.T)/(-2.0*k**2))
    return weights

def local_weight(point,xmat,k,ymat):
    '''
        This function calculates the local weight of each point in the dataset with respect to the point under consideration.
    '''

    # Retrieves the weight kernel of each point wrt the given point.
    wei = kernel(point,xmat,k)

    # Calculates the weight matrix for the particular point
    w = (xmat.T*(wei*xmat)).I*(xmat.T*(wei*ymat.T))
    return w

def local_weight_regression(xmat,ymat,k):
    '''
        Performs Local Weight Regression on each point of the dataset.
    '''
    m,n = np.shape(xmat)

    #  Defines the matrix which returns the predricted point for each input feature.
    ypred = np.zeros(m)
    for i in range(m):

        # the given point multiplied by the weight matrix values gives the predicted value.
        ypred[i] = xmat[i]*local_weight(xmat[i],xmat,k,ymat)

    return ypred
if __name__ == '__main__':
    data = pd.read_csv('tips.csv')
    bill = data.total_bill
    tip = data.tip

    mbill = np.mat(bill)
    mtip = np.mat(tip)

    m = np.shape(mbill)[1]

    one = np.mat(np.ones(m))

    x = np.hstack((one.T,mbill.T))
    ypred = local_weight_regression(x,mtip,10)

    sortIndex = x[:,1].argsort(0)
    xsort = x[sortIndex][:,0]

    plt.scatter(bill,tip,color='green')
    plt.plot(xsort[:,1],ypred[sortIndex],color='red',linewidth=5)
    plt.show()


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time     : 2018/12/27 09:54
# @Author   : Iydon
# @File     : course8.1.py

import numpy as np
import Poly

def least_square_poly(xs, ys, n, numpy, Poly, display=False, error=False):
    m = len(xs)
    A = numpy.zeros((n+1,n+1))
    b = numpy.zeros((n+1,1))
    for i in range(n+1):
        for j in range(n+1):
            A[i,j] = sum(map(lambda x:x**(i+j), xs))
        b[i] = sum([y*x**i for x,y in zip(xs,ys)])
    x = numpy.matmul(numpy.linalg.inv(A), b)
    f = Poly.Poly(x.T.tolist()[0][::-1])
    if display:
        print(A)
        print(b.T)
        print(x.T)
    if error:
        xs_ = [f.lambdify()(x) for x in xs]
        print(sum([(x-y)**2 for x,y in zip(xs_,ys)]))
    return f

xs = [0, 0.25, 0.50, 0.75, 1.00]
ys = [1.0000, 1.2840, 1.6487, 2.1170, 2.7183]
result = least_square_poly(xs, ys, 2, np, Poly, display=True, error=True)
print(result)

print()

xs = [1.0, 1.1, 1.3, 1.5, 1.9, 2.1]
ys = [1.84, 1.96, 2.21, 2.45, 2.94, 3.18]
for i in [1,2,3]:
    result = least_square_poly(xs, ys, i, np, Poly, display=True, error=True)
    print(result)

print()

E  = 5.3
ls = [7, 9.4, 12.3]
Fs = [2, 4, 6]
ks = [Fs[i]/(ls[i]-E) for i in range(len(ls))]
result = least_square_poly(ls, ks, 0, np, Poly, display=True, error=True)
print(result)

print()

ls += [8.3, 11.3, 14.4, 15.9]
Fs += [3, 5, 8, 10]
ks = [Fs[i]/(ls[i]-E) for i in range(len(ls))]
result = least_square_poly(ls, ks, 0, np, Poly, display=True, error=True)
print(result)


from pylab import *
import tables
import math
import numpy

def fxy(nm, nn, amn, bmn, XX, YY):
    f = zeros(XX.shape, float)
    for m in range(nm):
        for n in range(nn):
            f = f + amn[m][n]*cos(m*XX)*cos(n*YY) + bmn[m][n]*sin(m*XX)*sin(n*YY)
    return f/50

def sxy(nm, nn, amn, bmn, XX, YY):
    f = zeros(XX.shape, float)
    for m in range(nm):
        for n in range(nn):
            f = f + -(m*m+n*n)*(amn[m][n]*cos(m*XX)*cos(n*YY) + bmn[m][n]*sin(m*XX)*sin(n*YY))
    return f/50

amn = [[0,10,0], [10,0,0], [10,0,0]]
bmn = [[0,10,0], [10,0,0], [10,0,0]]

fh = tables.openFile("s101-periodic-poisson-2d_phi.h5")
q = fh.root.StructGridField
nx, ny, nc = q.shape

xup = 2*math.pi

# node 1
Xf = linspace(0, xup, nx)
Yf = linspace(0, xup, ny)
q_1 = q[:,:,0]
q_1 = q_1 - q_1.max()

dx = Xf[1]-Xf[0]
dy = Yf[1]-Yf[0]
hsize = math.sqrt(dx*dy)

XX, YY = meshgrid(Xf, Yf)

Xhr = linspace(0, xup, 101)
Yhr = linspace(0, xup, 101)
XXhr, YYhr = meshgrid(Xhr, Yhr)

fhr = fxy(3, 3, amn, bmn, XXhr, YYhr)
fhr = fhr - fhr.max()
vmin, vmax = fhr.min(), fhr.max()

# make plots
f1 = figure(1)

subplot(1,2,1)
cax = pcolormesh(Xf, Yf, transpose(q_1))
colorbar()
axis('image')

subplot(1,2,2)
cax = pcolormesh(Xhr, Yhr, fhr)
colorbar()
axis('image')

# compute error
fex = fxy(3, 3, amn, bmn, XX, YY)
fex = fex - fex.max()
error = numpy.abs(fex-transpose(q_1)).sum()/(nx*ny)

print hsize, error

# compute source
fh = tables.openFile("s101-periodic-poisson-2d_src.h5")
q = fh.root.StructGridField
src = q[:,:,0]

srcEx = sxy(3, 3, amn, bmn, XX, YY)

show()


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm

class ackley():
    '''
    -32.768 < x < 32.768
    fx = 0 at x = (0, 0, ..., 0)
    '''
    def __init__(self):
        self.domain = (-32.768, 32.768)
    def fx(self, x):
        t1 = -20* np.exp( -1/5 * np.sqrt( 1/len(x) * np.sum(x*x) ) )
        t2 = -np.exp(1/len(x) * np.sum( np.cos(2*np.pi*x) ) )
        t3 = 20
        t4 = np.e
        return t1 + t2 + t3 + t4

class sphere():
    '''
    -5.12 < x < 5.12
    fx = 0 at x = (0, 0, ..., 0)
    '''
    def __init__(self):
        self.domain = (-5.12, 5.12)
    def fx(self, x):
        return np.sum(x*x)

class rosenbrock():
    '''
    -5 < x < 5
    fx = 0 at x = (1, 1, ..., 1)
    '''
    def __init__(self):
        self.domain = (-5, 5)
    def fx(self, x):
        sum = 0
        for i in range(len(x)-1):
            sum = sum + np.power((x[i]-1), 2) + 100* np.power( x[i+1]- np.power(x[i],2) ,2)
        return sum

def select_function(func_name):
    if func_name == "ackley":
        return ackley()
    elif func_name == "sphere":
        return sphere()
    elif func_name == "rosenbrock":
        return rosenbrock()
    else:
        raise Exception("Incorrect function name.")

def plot2d(func):
    min = func.domain[0]
    max = func.domain[1]

    x = np.arange(min, max, 0.04)
    y = np.arange(min, max, 0.04)
    xx, yy = np.meshgrid(x, y)
    zz = np.empty(xx.shape)

    for i in range(zz.shape[0]):
        for j in range(zz.shape[1]):
            zz[i][j] = func.fx(np.array( [xx[i][j], yy[i][j]] ))

    fig = plt.figure()
    ax = fig.add_subplot(1, 1, 1, projection='3d')
    surf = ax.plot_surface(xx, yy, zz, rstride=1, cstride=1, cmap=cm.coolwarm,
                        linewidth=0, antialiased=False)
    fig.colorbar(surf, shrink=0.5, aspect=10)
    plt.show()

if __name__ == '__main__':
    func = select_function("sphere")
    plot2d(func)

    a = func.fx(np.array([0,0]))
    print(a)
    print(np.array([0,0]))


# Università di Bologna
# Corso di laurea in Informatica
# 02023 - Calcolo numerico
# 25/11/2021
#
# Stefano Volpe #969766
# lab5.py

import numpy as np
import matplotlib.pyplot as plt
from skimage import metrics
from skimage.data import camera
from numpy import fft
from scipy.optimize import minimize

def es0():
    '''
    Esercizio 1
    - Caricare l'immagine camera() dal modulo skimage.data, rinormalizzandola nel
      range.
    - Applicare un blur di tipo gaussiano con deviazione standard il cui
      kernel ha dimensioni utilizzando le funzioni fornite: gaussian_kernel(),
      psf_fft() ed A().
    - Aggiungere rumore di tipo gaussiano, con deviazione standard, usando la
      funzione np.random.normal().
    - Calcolare il Peak Signal Noise Ratio (PSNR) ed il Mean Squared Error (MSE)
      tra l'immagine degradata e l'immagine originale usando le funzioni
      peak_signal_noise_ratio e mean_squared_error disponibili nel modulo
      skimage.metrics.
    '''
    # Crea un kernel Gaussiano di dimensione kernlen e deviazione standard sigma
    def gaussian_kernel(kernlen, sigma):
        x = np.linspace( - (kernlen // 2), kernlen // 2, kernlen)
        # Kernel gaussiano unidmensionale
        kern1d = np.exp( - 0.5 * (x ** 2 / sigma))
        # Kernel gaussiano bidimensionale
        kern2d = np.outer(kern1d, kern1d)
        # Normalizzazione
        return kern2d / kern2d.sum()

    # Esegui l'fft del kernel K di dimensione d aggiungendo gli zeri necessari
    # ad arrivare a dimensione shape
    def psf_fft(kern2d, d, shape):
        # Aggiungi zeri
        K_p = np.zeros(shape)
        K_p[:d, :d] = kern2d

        # Sposta elementi
        p = d // 2
        K_pr = np.roll(np.roll(K_p, - p, 0), - p, 1)

        # Esegui FFT
        K_otf = fft.fft2(K_pr)
        return K_otf

    # Moltiplicazione per A
    def A(x, K):
        x = fft.fft2(x)
        return np.real(fft.ifft2(K * x))

    # Moltiplicazione per A trasposta
    # def AT(x, K):
        # x = fft.fft2(x)
        # return np.real(fft.ifft2(np.conj(K) * x))

    # Immagine in floating point con valori tra 0 e 1
    X = camera().astype(np.float64) / 255.0

    # Genera K, gli autovalori di A
    len = 24
    sigma = 3
    K = psf_fft(gaussian_kernel(len, sigma), len, X.shape)

    # Genera il rumore
    sigma2 = 0.02
    noise = np.random.normal(size = X.shape) * sigma2

    # Aggiungi blur e rumore
    b = A(X, K) + noise
    PSNR = metrics.peak_signal_noise_ratio(X, b)
    # ATb = AT(b, K)

    # Visualizziamo i risultati
    plt.figure(figsize = (30, 10))

    ax1 = plt.subplot(1, 2, 1)
    ax1.imshow(X, cmap = 'gray', vmin = 0, vmax = 1)
    plt.title('Immagine Originale')

    ax2 = plt.subplot(1, 2, 2)
    ax2.imshow(b, cmap = 'gray', vmin = 0, vmax = 1)
    #plt.title(f'Immagine Corrotta (PSNR: {PSNR:.2f})')

    plt.show()
    PSNR = metrics.peak_signal_noise_ratio(X, b)
    MSE = MSE = metrics.mean_squared_error(X, b)
    print('This is the MSE: ', MSE)
    print('This is the PSNR: ', PSNR)

def es1():
    '''
    Esercizio 2
    - Importare la function minimize da scipy.optimize e visualizzarne l'help.
    - Usando la function minimize con il metodo CG minimizzare la funzione
    f: R^n -> R definita come:
        f(x) = \\sum_i_n(x_i - 1)^2
    - Analizzare la struttura restituita in output dalla function minimize.
    '''
    def f(X):
        res = (X - np.ones(X.shape)) ** 2
        return np.sum(res)

    def df(X):
        res = 2 * (X-np.ones(X.shape))
        return res

    x0 = np.array([2, -10])
    res = minimize(f, x0, method = 'CG', jac = df)

    print(res)


"""
参照matlab绘制复变函数的例子，创建函数cplxgrid,cplxmap,cplxroot
https://www.jb51.net/article/174716.htm

"""
# 1.导入相关库
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import *

# 2.创建函数
def cplxgrid(m):
    """Return polar coordinate complex grid.

    Parameters
    ----------
    m: int

    Returns
    ----------
    z: ndarray,with shape (m+1)-by-(2*(m+1))
    """
    m = m
    r = np.arange(0, m).reshape(m, 1) / m
    theta = np.pi * np.arange(-m, m) / m
    z = r * np.exp(1j * theta)

    return z

def cplxroot(n=3, m=20):
    """
    cplxroot(n): renders the Riemann surface for the n-th root
    cplxroot(): renders the Riemann surface for the cube root.
    cplxroot(n,m): uses an m-by-m grid.  Default m = 20.

    Use polar coordinates, (r,theta).
    Use polar coordinates, (r,theta).

    Parameters
    ----------
    n: n-th root
    m: int

    Returns
    ----------
    None: Plot the Riemann surface
    """
    m = m + 1
    r = np.arange(0, m).reshape(m, 1) / m
    theta = np.pi * np.arange(-n * m, n * m) / m
    z = r * np.exp(1j * theta)
    s = r * (1 / n) * np.exp(1j * theta / n)
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    # ax.plot_surface(np.real(z),np.imag(z),np.real(s),color = np.imag(s))
    ax.plot_surface(np.real(z), np.imag(z), np.real(s), cmap=plt.cm.hsv)
    ax.set_xlim((-1, 1))
    ax.set_ylim((-1, 1))
    ax.set_xlabel('Real')
    ax.set_ylabel('Imag')
    ax.set_xticks([])
    ax.set_yticks([])
    ax.set_zticks([])
    ax.set_autoscalez_on(True)  # z轴自动缩放
    ax.grid('on')
    plt.show()

def cplxmap(z, cfun):
    '''
    Plot a function of a complex variable.

    Parameters
    ----------
    z: complex plane
    cfun: complex function to plot

    Returns
    ----------
    None: Plot the surface of complex function
    '''
    blue = 0.2
    x = np.real(z)
    y = np.imag(z)
    u = np.real(cfun)
    v = np.imag(cfun)
    M = np.max(np.max(u))  # 复变函数实部最大值
    m = np.min(np.min(u))  # 复变函数实部最大值
    s = np.ones(z.shape)
    fig = plt.figure()
    ax = fig.add_subplot(111, projection='3d')
    # 投影部分用线框图
    surf1 = ax.plot_wireframe(x, y, m * s, cmap=plt.cm.hsv)
    surf2 = ax.plot_surface(x, y, u, cmap=plt.cm.hsv)

    # 绘制复变函数1/z时会出错，ValueError: Axis limits cannot be NaN or Inf
    # ax.set_zlim(m, M)
    ax.set_xlim((-1, 1))
    ax.set_ylim((-1, 1))
    ax.set_xlabel('Real')
    ax.set_ylabel('Imag')
    ax.set_xticks([])
    ax.set_yticks([])
    ax.set_zticks([])
    ax.set_autoscalez_on(True)  # z轴自动缩放

    ax.grid('on')
    plt.show()

def _test_cplxmap():
    '''测试cplxmap函数'''
    z = cplxgrid(30)
    w1 = z
    w2 = z ** 2
    w3 = (z ** 4 - 1) ** (1 / 4)
    w4 = 1 / z
    w5 = np.arctan(2 * z)
    w6 = np.sqrt(z)
    w = [w1, w2, w3, w4, w5, w6]
    for i in w:
        cplxmap(z, i)

def _test_cplxroot():
    '''测试cplxroot函数'''
    cplxroot(n=2)
    cplxroot(n=3)
    cplxroot(n=4)
    cplxroot(n=5)

if __name__ == '__main__':
    _test_cplxmap()
    _test_cplxroot()


"""
Converts a LIDAR LAS file to an
ASCII DEM.  Interpolation is used
to account for data loss.
"""
# We only need 2 modules
# both available on PyPI!
from laspy.file import File
import numpy as np

# Source LAS file
source = "lidar.las"

# Output ASCII DEM file
target = "lidar.asc"

# Grid cell size (data units)
cell = 1.0

# No data value for output DEM
NODATA = 0

# Open LIDAR LAS file
las = File(source, mode="r")

#xyz min and max
min = las.header.min
max = las.header.max

# Get the x axis distance
xdist = max[0] - min[0]

# Get the y axis distance
ydist = max[1] - min[1]

# Number of columns for our grid
cols = int(xdist) / cell

# Number of rows for our grid
rows = int(ydist) / cell

cols += 1
rows += 1

# Track how many elevation
# values we aggregate
count = np.zeros((rows, cols)).astype(np.float32)
# Aggregate elevation values
zsum = np.zeros((rows, cols)).astype(np.float32)

# Y resolution is negative
ycell = -1 * cell

# Project x,y values to grid
projx = (las.x - min[0]) / cell
projy = (las.y - min[1]) / ycell
# Cast to integers and clip for use as index
ix = projx.astype(np.int32)
iy = projy.astype(np.int32)

# Loop through x,y,z arrays, add to grid shape,
# and aggregate values for averaging
for x,y,z in np.nditer([ix, iy, las.z]):
  count[y, x]+=1
  zsum[y, x]+=z

# Change 0 values to 1 to avoid numpy warnings,
# and NaN values in array
nonzero = np.where(count>0, count, 1)
# Average our z values
zavg = zsum/nonzero

# Interpolate 0 values in array to avoid any
# holes in the grid
mean = np.ones((rows,cols)) * np.mean(zavg)
left = np.roll(zavg, -1, 1)
lavg = np.where(left>0,left,mean)
right = np.roll(zavg, 1, 1)
ravg = np.where(right>0,right,mean)
interpolate = (lavg+ravg)/2
fill=np.where(zavg>0,zavg,interpolate)

# Create our ASCII DEM header
header = "ncols        %s\n" % fill.shape[1]
header += "nrows        %s\n" % fill.shape[0]
header += "xllcorner    %s\n" % min[0]
header += "yllcorner    %s\n" % min[1]
header += "cellsize     %s\n" % cell
header += "NODATA_value      %s\n" % NODATA

# Open the output file, add the header, save the array
with open(target, "wb") as f:
  f.write(header)
  # The fmt string ensures we output floats
  # that have at least one number but only
  # two decimal places
  np.savetxt(f, fill, fmt="%1.2f")


# --------------
import pandas as pd
import numpy as np
from scipy import stats
import math

data = pd.read_csv(path)

sample_size = 2000
data_sample = data.sample(n = sample_size, random_state = 0)
sample_mean = data_sample['installment'].mean()
sample_std = data_sample['installment'].std()

z_critical = stats.norm.ppf(q=0.95)
margin_of_error = z_critical*(sample_std / math.sqrt(sample_size))

confidence_interval = (sample_mean - margin_of_error , sample_mean + margin_of_error)

true_mean = data['installment'].mean()

print("Value of True mean :",true_mean)
print("Confidence intervals :",confidence_interval)
print("Value of sample mean :",sample_mean)

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

#Different sample sizes to take
sample_size=np.array([20,50,100])
fig,axes = plt.subplots(3,1,)
#Code starts here

for i in range(len(sample_size)):
    m = []
    for j in (range(1000)):
        sample_data = data['installment'].sample(n= sample_size[i])
        m.append(sample_data.mean())
    mean_series = pd.Series(m)

axes[0].hist(mean_series)
axes[1].hist(mean_series)
axes[2].hist(mean_series)

# --------------
#Importing header files

from statsmodels.stats.weightstats import ztest

#Code starts here

data['int.rate'] =  pd.to_numeric(data['int.rate'].map(lambda x: x.rstrip("%"))).apply(lambda x : x/100)
data['int.rate'].unique()
data['int.rate'].describe()

z_statistic, p_value = ztest(x1 = data[data['purpose']=='small_business']['int.rate'], value = data['int.rate'].mean(), alternative =                                    'larger')
print(z_statistic,p_value)

if p_value < 0.05:
    print("we reject the null hypothesis")
if p_value > 0.05:
    print("we accept the null hypothesis")

# --------------
#Importing header files
from statsmodels.stats.weightstats import ztest

#Code starts here

z_statistic, p_value = ztest(x1 = data[data['paid.back.loan']=='No']['installment'], x2 = data[data['paid.back.loan']=='Yes']                                           ['installment'],alternative='two-sided')

print(z_statistic,p_value)

if p_value < 0.05:
    print("we can reject the null hypothesis")
if p_value > 0.05:
    print("we cannot reject the null hypothesis")

# --------------
#Importing header files
from scipy.stats import chi2_contingency

#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

#Code starts here
yes = data[data['paid.back.loan'] == 'Yes']['purpose'].value_counts()
no = data[data['paid.back.loan']=='No']['purpose'].value_counts()
print("have paid back loan:",yes)
print("have not paid back loan:",no)
observed = pd.concat([yes.transpose(),no.transpose()], axis = 1, keys = ['Yes','No'])

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

if chi2 > critical_value:
    print("we reject the null hypothesis")
if chi2 < critical_value:
    print("null hypothsis cannot be rejected")

import numpy,from scipy,from statsmodels


#Ref: Sreenivas Sarwar Anik

"""
This code demonstrates potential applications in image processing by manipulating DCT.
In this example, we perform averaging of images in real and DCT space.
Since DCT is linear, an average of images in spatial domain would be identical to performing
DCT on each image and averaging the DCT and then inverse DCT to bring back the original image.

DCT is similar to DFT. DFT uses a set of complex exponential functions,
while the DCT uses only (real-valued) cosine functions.

DCT is used in data compression applications such as JPEG.This is becasue DCT representation
has more of the information concentrated in small number of coefficients. This is useful for
compression algorithms because you can approximate original information in a relatively small set
of DCT coefficients.
"""

import matplotlib.pyplot as plt
import cv2
import numpy as np
from scipy.fftpack import dct, idct

from os import listdir

image_dir = "C:/PfM/Python_files/New_stuff/noisy_images/25_sigma/"  # Path to image directory

# Load images
filenames = listdir(image_dir)
filenames.sort()

imgs = []
for f in filenames:
	imgs.append((cv2.imread(image_dir + f, 0)).astype(np.float32))

height, width = imgs[0].shape

# Apply the weighted average to images and corresponding DCT images, respectively.
avg_img = np.zeros([height, width], np.float32)
dct_avg_img = np.zeros([height, width], np.float32)

for i in range(len(imgs)):
    avg_img = cv2.addWeighted(avg_img, i/(i+1.0), imgs[i], 1/(i+1.0), 0) #Original image average
    dct_avg_img = cv2.addWeighted(dct_avg_img, i/(i+1.0), dct(imgs[i]), 1/(i+1.0), 0) #DCT average

reverse_img = idct(dct_avg_img) #Convert averaged DCT back to real space.

plt.imsave("noisy_images/00-dct_averaged_img.jpg", reverse_img, cmap="gray")
plt.imsave("noisy_images/00-averaged_img.jpg", avg_img, cmap="gray")

fig = plt.figure(figsize=(10, 10))
ax1 = fig.add_subplot(2,2,1)
ax1.imshow(imgs[0], cmap='gray')
ax1.title.set_text('Input Image 1')
ax2 = fig.add_subplot(2,2,2)
ax2.imshow(imgs[1], cmap='gray')
ax2.title.set_text('Input Image 2')

ax3 = fig.add_subplot(2,2,3)
ax3.imshow(avg_img, cmap='gray')
ax3.title.set_text('Average of Images')
ax4 = fig.add_subplot(2,2,4)
ax4.imshow(reverse_img, cmap='gray')
ax4.title.set_text('Image from DCT average')
plt.show()


import numpy as np
import operations as op

print("Please enter size of your Matrix. ")

size = input("Size(m*n): ").split(' ')
m = int(size[0])
n = int(size[1])

entries = []    # array for elements of matrix

for row in range(m):
    entries = entries + list(map(float, input().split()))     # input elements of each line
matrix_A = np.array(entries).reshape(m, n)      # create n * n array with numpy library by using reshape func

zero_vector = np.zeros((m, 1), dtype=float)

augmented_matrix = np.column_stack((matrix_A, zero_vector))     # create augmented matrix of [A | 0]

pivot_positions = []    # list of pivot positions

pivot_positions = op.make_reduced_echelon(augmented_matrix, m, n, pivot_positions)  # convert augmented matrix to RREF

print("\n\n")
op.display_matrix(augmented_matrix, m, n)
print("\n\n")

row_bases_vector = []
column_bases_vector = []
null_bases_vector = {}  # key is number of column and value is column
dependent_vector = []

for row_number in op.detect_nonzero_row(augmented_matrix, m, n):  # detect nonzero row in RREF form
    row_bases_vector.append(augmented_matrix[row_number][:-1])

print("Row Bases : \n")
op.display_set_vectors(row_bases_vector, len(row_bases_vector), n)   # display row bases

pivot_columns_number = []  # list of number of columns that pivot
for row, column in pivot_positions:
    pivot_columns_number.append(column)
    column_bases_vector.append(matrix_A[:, column])

print('\n\n')
print("Column Bases : \n")
op.display_set_vectors(column_bases_vector, len(column_bases_vector), m)    # display column bases

dependent_columns_number = set(list(range(n))) - set(pivot_columns_number)  # list of number of dependent columns
for column in dependent_columns_number:
    null_bases_vector[column] = list(augmented_matrix[:, column])
    dependent_vector.append(matrix_A[:, column])

print('\n\n')
print("Null Bases : \n")
op.display_set_vectors(op.make_null_vectors(null_bases_vector, dependent_columns_number), n-len(column_bases_vector), n)

column_bases_vector = np.array(column_bases_vector).transpose()

coordinates = []
# coordinates of linear combination
for vector in dependent_vector:
    pivot_positions = []
    augmented_matrix_2 = np.column_stack((column_bases_vector, np.array(vector)))
    op.make_reduced_echelon(augmented_matrix_2, m, len(pivot_columns_number), pivot_positions)
    coordinate = augmented_matrix_2[:, -1]
    for number in range(len(coordinate)):
        coordinate[number] = round(coordinate[number], 2)
    coordinates.append(coordinate)

print("\nLinear Combination of dependent vectors : \n")
for vector in range(len(dependent_vector)):
    print(f"{dependent_vector[vector]}   = ", end='')
    for index in range(len(column_bases_vector)):
        if coordinates[vector][index] == 0:
            continue
        print(f"{coordinates[vector][index]} * {column_bases_vector[:, index]} ", end='   ')
    print("\n\n")


import numpy as np

K = 0.5

"""Return the slope calculated between two data points in 2-Dimensional space"""

def get_slope(x1, x2, y1, y2):
    return (y1 - y2) / (x1 - x2)

"""Return the Euclidean distance between two points"""

def euclidean(x1, x2, y1, y2):
    _y = (y1 - y2) * (y1 - y2)
    _x = (x1 - x2) * (x1 - x2)
    return np.sqrt(_x + _y)

"""Helper functions to fit on the detected flares"""

def exp_fit_func(x, ln_a, b):
    t = x**K
    return ln_a - b * t

def exp_func(x, a, b):
    t = -1 * b * (x**K)
    return a * np.exp(t)

def inverse_exp_func(y, a, b):
    t1 = np.log(y) - np.log(a)
    t2 = -1 * t1 / b
    return int(t2 ** (1.0 / K))


import numpy as np

def softmax(predictions):
    '''
    Computes probabilities from scores
    Arguments:
      predictions, np array, shape is either (N) or (batch_size, N) -
        classifier output
    Returns:
      probs, np array of the same shape as predictions -
        probability for every class, 0..1
    '''
    # TODO implement softmax
    # Your final implementation shouldn't have any loops
    if predictions.ndim == 1:
        predictions_normalized = predictions.copy() - predictions.max()
        predictions_exp = np.exp(predictions_normalized)

        exp_sum = predictions_exp.sum()
        results = predictions_exp / exp_sum
    else:
        predictions_normalized = predictions.copy() - predictions.max(axis=1).reshape((-1, 1))
        predictions_exp = np.exp(predictions_normalized)

        exp_sum = predictions_exp.sum(axis=1)
        results = predictions_exp / exp_sum.reshape((-1, 1))

    return results

def l2_regularization(W, reg_strength):
    '''
    Computes L2 regularization loss on weights and its gradient

    Arguments:
      W, np array - weights
      reg_strength - float value

    Returns:
      loss, single value - l2 regularization loss
      gradient, np.array same shape as W - gradient of weight by l2 loss
    '''
    # TODO: Copy from previous assignment
    loss = reg_strength * np.power(W, 2).sum()
    grad = reg_strength * 2 * W

    return loss, grad

def cross_entropy_loss(probs, target_index):
    '''
    Computes cross-entropy loss
    Arguments:
      probs, np array, shape is either (N) or (batch_size, N) -
        probabilities for every class
      target_index: np array of int, shape is (1) or (batch_size) -
        index of the true class for given sample(s)
    Returns:
      loss: single value
    '''
    # TODO implement cross-entropy
    # Your final implementation shouldn't have any loops
    if probs.ndim == 1:
        return -np.log(probs[target_index])
    loss = 0.0
    for i in range(probs.shape[0]):
        loss -= np.log(probs[i][target_index[i]])
    return loss

def softmax_with_cross_entropy(preds, target_index):
    '''
    Computes softmax and cross-entropy loss for model predictions,
    including the gradient

    Arguments:
      predictions, np array, shape is either (N) or (batch_size, N) -
        classifier output
      target_index: np array of int, shape is (1) or (batch_size) -
        index of the true class for given sample(s)

    Returns:
      loss, single value - cross-entropy loss
      dprediction, np array same shape as predictions - gradient of predictions by loss value
    '''
    # TODO copy from the previous assignment
    probs = softmax(preds)
    loss = cross_entropy_loss(probs, target_index)
    dprediction = probs.copy()
    if preds.ndim == 1:
        dprediction[target_index] -= 1
        return loss, dprediction
    else:
        for ind, value in enumerate(target_index):
            dprediction[ind, value] -= 1
        return loss / probs.shape[0], dprediction / probs.shape[0]

class Param:
    '''
    Trainable parameter of the model
    Captures both parameter value and the gradient
    '''
    def __init__(self, value):
        self.value = value
        self.grad = np.zeros_like(value)

class ReLULayer:
    def __init__(self):
        pass

    def forward(self, X):
        # TODO copy from the previous assignment
        self.X = X
        result = np.maximum(X, 0)
        return result

    def backward(self, d_out):
        # TODO copy from the previous assignment
        d_result = (self.X >= 0) * 1
        d_result = np.multiply(d_out, d_result)
        return d_result

    def params(self):
        return {}

class FullyConnectedLayer:
    def __init__(self, n_input, n_output):
        self.W = Param(0.001 * np.random.randn(n_input, n_output))
        self.B = Param(0.001 * np.random.randn(1, n_output))
        self.X = None

    def forward(self, X):
        # TODO copy from the previous assignment
        self.X = Param(X)
        result = X.dot(self.W.value) + self.B.value
        return result

    def backward(self, d_out):
        # TODO copy from the previous assignment

        dW = self.X.value.T.dot(d_out)
        dX = d_out.dot(self.W.value.T)
        dB = d_out.sum(axis=0).reshape((1, -1))
        # It should be pretty similar to linear classifier from
        # the previous assignment
        self.B.grad += dB
        self.W.grad += dW
        d_input = dX
        return d_input

    def params(self):
        return { 'W': self.W, 'B': self.B }

class ConvolutionalLayer:
    def __init__(self, in_channels, out_channels,
                 filter_size, padding):
        '''
        Initializes the layer

        Arguments:
        in_channels, int - number of input channels
        out_channels, int - number of output channels
        filter_size, int - size of the conv filter
        padding, int - number of 'pixels' to pad on each side
        '''

        self.filter_size = filter_size
        self.in_channels = in_channels
        self.out_channels = out_channels
        self.W = Param(
            np.random.randn(filter_size, filter_size,
                            in_channels, out_channels)
        )

        self.B = Param(np.zeros(out_channels))
        self.padding = padding

    def forward(self, X):
        X_padded = np.pad(X, pad_width=((0, 0), (self.padding, self.padding), (self.padding, self.padding), (0, 0)), constant_values=0)
        batch_size, height, width, channels = X.shape
        self.X = Param(X)
        self.X_padded = Param(X_padded)

        out_height = height - self.filter_size + 2 * self.padding + 1
        out_width = width - self.filter_size + 2 * self.padding + 1

        # TODO: Implement forward pass
        # Hint: setup variables that hold the result
        # and one x/y location at a time in the loop below
        result = np.zeros((batch_size, out_height, out_width, self.out_channels))
        # It's ok to use loops for going over width and height
        # but try to avoid having any other loops
        for y in range(out_height):
            for x in range(out_width):
                # TODO: Implement forward pass for specific location
                receptive_field = X_padded[:, y: y + self.filter_size, x: x + self.filter_size, :].reshape((batch_size, self.filter_size * self.filter_size * channels))
                W_reshaped = self.W.value.reshape(self.filter_size*self.filter_size*self.in_channels, self.out_channels)
                result[:, y, x, :] = receptive_field @ W_reshaped + self.B.value

        return result

    def backward(self, d_out):
        # Hint: Forward pass was reduced to matrix multiply
        # You already know how to backprop through that
        # when you implemented FullyConnectedLayer
        # Just do it the same number of times and accumulate gradients

        batch_size, height, width, channels = self.X_padded.value.shape
        _, out_height, out_width, out_channels = d_out.shape
        # TODO: Implement backward pass
        # Same as forward, setup variables of the right shape that
        # aggregate input gradient and fill them for every location
        # of the output
        fs = self.filter_size
        # Try to avoid having any other loops here too
        for y in range(out_height):
            for x in range(out_width):
                dd_out = d_out[:, y, x, :] # (bs, out_chan)

                receptive_field = self.X_padded.value[:, y: y + fs, x: x + fs, :] # (bs, fs, fs, in_chan)
                receptive_field_reshaped = receptive_field.reshape((batch_size, fs * fs * channels)) # (bs, fs*fs*in_chan)

                W_grad_reshaped = receptive_field_reshaped.T.dot(dd_out) # (fs*fs*in_chan, out_chan)
                self.W.grad += W_grad_reshaped.reshape((fs, fs, channels, out_channels)) # (fs, fs, in_chan, out_chan)

                X_grad_reshaped = dd_out.dot(self.W.value.reshape(fs*fs*self.in_channels, self.out_channels).T) # (bs, fs*fs*in_chan)

                self.X_padded.grad[:, y:y + fs, x:x + fs, :] += X_grad_reshaped.reshape((batch_size, fs, fs, channels))
                self.B.grad += dd_out.sum(axis=0)

        if self.padding != 0:
            d_input = self.X_padded.grad[:, self.padding: -self.padding, self.padding: -self.padding, :]
        else:
            d_input = self.X_padded.grad

        self.X.grad = d_input

        return d_input

    def params(self):
        return { 'W': self.W, 'B': self.B }

class MaxPoolingLayer:
    def __init__(self, pool_size, stride):
        '''
        Initializes the max pool

        Arguments:
        pool_size, int - area to pool
        stride, int - step size between pooling windows
        '''
        self.pool_size = pool_size
        self.stride = stride
        self.X = None

    def forward(self, X):
        batch_size, height, width, channels = X.shape
        # TODO: Implement maxpool forward pass
        # Hint: Similarly to Conv layer, loop on
        # output x/y dimension
        self.X = Param(X)
        out_height = int((height - self.pool_size) / self.stride + 1)
        out_width = int((width - self.pool_size) / self.stride + 1)

        # TODO: Implement forward pass
        # Hint: setup variables that hold the result
        # and one x/y location at a time in the loop below
        result = np.zeros((batch_size, out_height, out_width, channels))
        for y in range(out_height):
            for x in range(out_width):
                result[:, y, x, :] = np.max(X[:, y*self.stride: y*self.stride + self.pool_size, x*self.stride: x*self.stride + self.pool_size, :], axis=(1, 2))

        return result

    def backward(self, d_out):
        # TODO: Implement maxpool backward pass
        ps = self.pool_size
        stride = self.stride

        batch_size, height, width, channels = self.X.value.shape
        d_input = np.zeros_like(self.X.value)

        out_height = int((height - ps) / stride + 1)
        out_width = int((width - ps) / stride + 1)

        for y in range(out_height):
            for x in range(out_width):
                window = self.X.value[:, y*stride: y*stride + ps, x*stride: x*stride + ps, :]

                d_input[:, y*stride: y*stride + ps, x*stride: x*stride + ps, :] = (np.max(window, axis=(1, 2), keepdims=True) == window) * d_out[:, y, x, :].reshape((batch_size, 1, 1, channels))

        return d_input

    def params(self):
        return {}

class Flattener:
    def __init__(self):
        self.X_shape = None

    def forward(self, X):
        batch_size, height, width, channels = X.shape

        # TODO: Implement forward pass
        # Layer should return array with dimensions
        # [batch_size, hight*width*channels]
        self.X_shape = X.shape
        return X.reshape((batch_size, height*width*channels))

    def backward(self, d_out):
        # TODO: Implement backward pass
        return d_out.reshape((self.X_shape))

    def params(self):
        # No params!
        return {}


import matplotlib
from matplotlib.figure import Figure
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
matplotlib.use('TkAgg')
import numpy as np
from numpy import *
from sympy import *
from scipy.integrate import quad
from tkinter import *
from tkinter.ttk import Combobox
from tkinter.messagebox import showwarning

class RectangleG ( object ) :
    def __init__ (self , a , b , n , f ) :
        self.a = a
        self.b = b
        self.x = np.linspace( a , b , n+1 )
        self.f = f
        self.n = n
    def integrate ( self , f ) :
        x= self.x
        y= f( x )
        h = float( x[1] - x[0] )
        s = sum( y[ 0 : -1 ] )
        return h * s
    def Graph ( self , f , resolution =1001 ) :
        xl = self.x
        yl = f(xl)
        xlist_fine =np.linspace( self.a , self.b , resolution )
        for i in range ( self.n ) :
            x_rect = [xl[ i ] , xl[ i ] , xl[ i + 1 ] , xl[i+1] , xl[ i ] ] # abscisses des sommets
            y_rect = [0 , yl[ i ] , yl[ i ] , 0 , 0 ] # ordonnees des sommets
            plt.plot ( x_rect , y_rect , 'r' )
        yflist_fine = f ( xlist_fine )
        plt.plot ( xlist_fine , yflist_fine )
        plt.plot(xl, yl,"bo")
        plt.xlabel ( 'x' )
        plt.ylabel ( ' f ( x ) ' )
        plt.title('Methode des Rectangles Gauches, N = {}'.format(self.n))

class Milieu(object):
    def __init__(self, a, b, n, f):
        self.a = a
        self.b = b
        self.x = np.linspace(a, b, n+1)
        self.f = f
        self.n = n
    def integrate(self,f):
        h = float(self.b-self.a)/self.n
        result = 0
        for i in range(self.n):
            result += f((self.a + h/2.0) + i*h)
        result *= h
        return result
    def Graph ( self , f , resolution =1001 ) :
        xl = self.x
        yl = f(xl)
        xlist_fine =np.linspace( self.a , self.b , resolution )
        for i in range ( self.n ) :
            mi = (xl[i]+xl[i+1])/2
            x_rect = [xl[ i ] , xl[ i ],xl[ i+1 ],xl[ i+1 ],xl[ i ]] # abscisses des sommets
            y_rect = [0 , f(mi),f(mi),0,0] # ordonnees des sommets
            plt.plot(x_rect,y_rect,'r')
            plt.plot ( mi , f(mi) , 'bo' )
        yflist_fine = f ( xlist_fine )
        plt.plot ( xlist_fine , yflist_fine )
        plt.xlabel ( 'x' )
        plt.ylabel ( ' f ( x ) ' )
        plt.title('Methode de Point Milieu, N = {}'.format(self.n))

class Trapezoidal(object):
    def __init__(self, a, b, n, f):
        self.a = a
        self.b = b
        self.x = np.linspace(a, b, n+1)
        self.f = f
        self.n = n
    def integrate(self,f):
        x=self.x
        y=f(x)
        h = float(x[1] - x[0])
        s = y[0] + y[-1] + 2.0*sum(y[1:-1])
        return h * s / 2.0
    def Graph(self,f,resolution=1001):
        xl = self.x
        yl = f(xl)
        xlist_fine=np.linspace(self.a, self.b, resolution)
        for i in range(self.n):
            x_rect = [xl[i], xl[i], xl[i+1], xl[i+1], xl[i]] # abscisses des sommets
            y_rect = [0   , yl[i], yl[i+1]  , 0     , 0   ] # ordonnees des sommets
            plt.plot(x_rect, y_rect,"r")
        yflist_fine = f(xlist_fine)
        plt.plot(xlist_fine, yflist_fine)#plot de f(x)
        plt.plot(xl, yl,"cs")#point support
        plt.ylabel ( ' f ( x ) ' )
        plt.title('Methode des Trapézes, N = {}'.format(self.n))

class Simps(object):
    def __init__(self, a, b, n, f): #initialiser les paramètres du classe
        self.a = a
        self.b = b
        self.x = np.linspace(a, b, n+1)#les pts supports
        self.f = f
        self.n = n #nombre de subdivision

    def integrate(self,f):#calculer la somme ((b-a)/6*n)*[f(a)+2*sum(xi)+4*sum(mi)+f(b)]
        x=self.x #les points supports xi #x(0)=a-->x(n)=b
        y=f(x) #yi variable local y(o)=f(xo)-->y(n)
        h = float(x[1] - x[0])#pas h=(b-a)/2*n
        n = len(x) - 1#nombre subdivision
        if n % 2 == 1:#si le reste de la division =1 impaire
            n -= 1
        s = y[0] + y[n] + 4.0 * sum(y[1:-1:2]) + 2.0 * sum(y[2:-2:2])
        return h * s / 3.0
    def Graph(self,f,resolution=1001):
        xl = self.x
        yl = f(xl)
        xlist_fine=np.linspace(self.a, self.b, resolution)
        for i in range(self.n):
            xx=np.linspace(xl[i], xl[i+1], resolution)
            m=(xl[i]+xl[i+1])/2#pt milieu
            aa=xl[i]#borne gauche
            bb=xl[i+1]#borne droite
            l0 = (xx-m)/(aa-m)*(xx-bb)/(aa-bb)
            l1 = (xx-aa)/(m-aa)*(xx-bb)/(m-bb)
            l2 = (xx-aa)/(bb-aa)*(xx-m)/(bb-m)
            P = f(aa)*l0 + f(m)*l1 + f(bb)*l2#
            plt.plot(xx,P,'r')
            plt.plot(m,f(m),"r",marker="s")
        yflist_fine = f(xlist_fine)#fontion f
        plt.plot(xlist_fine, yflist_fine,'b')
        plt.plot(xl, yl,'bo')#point support en bleu rond
        plt.ylabel('f(x)')
        plt.title('Methode de Simpson, N = {}'.format(self.n))

class mclass:

    def __init__(self,  window):

        self.window = window
        self.window.title("Projet Analyse Numérique")
        self.window.geometry("1366x768")
        self.fr1 = Frame(window,highlightbackground="DarkOrange1", highlightthickness=2, width=100, height=200, bd= 5) # Frame à gauche
        self.fr2 = Frame(window,highlightbackground="black", highlightthickness=2, width=100, height=200, bd= 5) # Frame au milieu
        self.fr3 = Frame(window,highlightbackground="brown1", highlightthickness=2, width=200, height=300, bd= 5) # Frame à droite
        self.func_txt=StringVar()
        self.func_txt.set("L'expression de f :")
        self.label_func=Label(self.fr1, textvariable=self.func_txt,justify=RIGHT, height=4, font=("Arial", 12))
        self.label_func.grid(row=1,column=0)

        self.a_txt=StringVar()
        self.a_txt.set("A:")
        self.label_a=Label(self.fr1, textvariable=self.a_txt,justify=RIGHT, anchor="w", height=4, font=("Arial", 12))
        self.label_a.grid(sticky = E,row=2,column=0)
        self.boxa = Entry(self.fr1,width=10,borderwidth=3,bg="powder blue")
        self.boxa.grid(sticky = W,row=2,column=1)
        self.b_txt=StringVar()
        self.b_txt.set("B:")
        self.label_b=Label(self.fr1, textvariable=self.b_txt,justify=RIGHT, anchor="w", height=4, font=("Arial", 12))
        self.label_b.grid(sticky = E,row=3,column=0)
        self.boxb = Entry(self.fr1,width=10,borderwidth=3,bg="powder blue")
        self.boxb.grid(sticky = W,row=3,column=1)
        self.box = Entry(self.fr1,borderwidth=3,bg="white")
        self.box.grid(row=1,column=1)

        # Dérivé
        self.diriv_txt=StringVar()
        self.diriv_txt.set("Dérivé: ")
        self.label_diriv = Label(self.fr1, textvariable=self.diriv_txt,justify=RIGHT, anchor="w", height=4, font=("Arial", 12))
        self.label_diriv.grid(sticky = E, row=5, column=0)

        self.res_txt=StringVar()
        self.res_diriv = Label(self.fr1,textvariable=self.res_txt,justify=RIGHT, anchor="w", width=10,borderwidth=3,bg="powder blue")
        self.res_diriv.grid(sticky = W, row=5, column=1)

        # Slider pour le dérivé
        self.s_txt=StringVar()
        self.s_txt.set("Ordre du Dérivé: ")
        self.label_s = Label(self.fr1, textvariable=self.s_txt,justify=RIGHT, anchor="w", height=4, font=("Arial", 12))
        self.label_s.grid(sticky = E, row=4, column=0)

        self.slider = Scale(self.fr1, from_=1, to=5, orient=HORIZONTAL)
        self.slider.grid(sticky = W, row=4, column=1)

        # Prémitive
        self.pri_txt=StringVar()
        self.pri_txt.set("Primitive: ")
        self.label_pri = Label(self.fr1, textvariable=self.pri_txt,justify=RIGHT, anchor="w", font=("Arial", 12))
        self.label_pri.grid(sticky = E, row=6, column=0)

        self.resp_txt=StringVar()
        self.res_prim = Label(self.fr1,textvariable=self.resp_txt,justify=RIGHT, anchor="w", width=10,borderwidth=3,bg="powder blue")
        self.res_prim.grid(sticky = W, row=6, column=1)

        # Check_Buttons

            # Afficher (ou pas) F(x)
        self.chbf_txt=StringVar()
        self.chbf_txt.set("Afficher F(x) ")
        self.label_chbf = Label(self.fr1, textvariable=self.chbf_txt,justify=RIGHT, anchor="w", font=("Arial", 12))
        self.label_chbf.grid(sticky = E, row=7, column=0)

        self.chbf=BooleanVar()
        self.res_chbf = Checkbutton(self.fr1,var=self.chbf,justify=RIGHT, anchor="w", width=10,borderwidth=3, command = self.afficher_F,
                                                                                activebackground="blue")
        self.res_chbf.grid(sticky = W, row=7, column=1)

            # Afficher (ou pas) le dérivé
        self.chbd_txt=StringVar()
        self.chbd_txt.set("Afficher le dérivé ")
        self.label_chbd = Label(self.fr1, textvariable=self.chbd_txt,justify=RIGHT, anchor="w", font=("Arial", 12))
        self.label_chbd.grid(sticky = E, row=8, column=0)

        self.chbd=BooleanVar()
        self.chbd.set(False)
        self.res_chbd = Checkbutton(self.fr1, var=self.chbd,justify=RIGHT, anchor="w", width=10,borderwidth=3, command = self.afficher_D,
                                                                                activebackground="green")
        self.res_chbd.grid(sticky = W, row=8, column=1)

            # Afficher (ou pas) le primitive
        self.chbp_txt=StringVar()
        self.chbp_txt.set("Afficher le prémitive ")
        self.label_chbp = Label(self.fr1, textvariable=self.chbp_txt,justify=RIGHT, anchor="w", font=("Arial", 12))
        self.label_chbp.grid(sticky = E, row=9, column=0)

        self.chbp=BooleanVar()
        self.res_chbp = Checkbutton(self.fr1, var=self.chbp,justify=RIGHT, anchor="w", width=10,borderwidth=3, command = self.afficher_P,
                                                                                activebackground="red")
        self.res_chbp.grid(sticky = W, row=9, column=1)

        # Button
        self.button = Button (self.fr1, width =15,text="Afficher",bg="powder blue", command=self.plot, font=("Arial", 12))
        self.button.grid(row=10,column=0,columnspan=3)
        self.fr1.grid(row=1,column=0,padx=10,pady=10,sticky="ns")
        self.fr2.grid(row=1,column=1,padx=10,pady=10)

        # Partie pour le calcule d'intégrale

            # Label
        self.integ_txt=StringVar()
        self.integ_txt.set("Si tu veux calculer la V.A de l'intégrale: ")
        self.label_integ = Label(self.fr3, textvariable=self.integ_txt,justify=RIGHT, anchor="w", height=4, font=("Arial", 12))
        self.label_integ.grid(row=1,column=0,columnspan=3)

            # ComboBox
        self.meth_txt=StringVar()
        self.meth_txt.set("Choisir une méthode: ")
        self.label_meth = Label(self.fr3, textvariable=self.meth_txt,justify=RIGHT, anchor="w",height=4, font=("Arial", 12))
        self.label_meth.grid(sticky = E, row=2, column=0)

        self.meth = Combobox(self.fr3,values=["---","Rectangle", "Trapéze", "Simpson", "Point Milieu"], state="readonly")
        self.meth.current(0)
        self.meth.grid(sticky = W, row=2, column=1)

            # Borne Inf :
        self.integ_txt=StringVar()
        self.integ_txt.set("Donner borne inf: ")
        self.label_integ = Label(self.fr3, textvariable=self.integ_txt,justify=RIGHT, anchor="w",height=4, font=("Arial", 12))
        self.label_integ.grid(sticky = E,row=3,column=0)

        self.boxinf = Entry(self.fr3,width=10,borderwidth=3,bg="yellow green")
        self.boxinf.grid(sticky = W,row=3,column=1)

            # Borne Sup :
        self.integ_txt_2=StringVar()
        self.integ_txt_2.set("Donner borne sup: ")
        self.label_integ_2 = Label(self.fr3, textvariable=self.integ_txt_2,justify=RIGHT, anchor="w",height=4, font=("Arial", 12))
        self.label_integ_2.grid(sticky = E,row=4,column=0)

        self.boxsup = Entry(self.fr3,width=10,borderwidth=3,bg="yellow green")
        self.boxsup.grid(sticky = W,row=4,column=1)

            # Slider pour nombre de divison
        self.n_txt=StringVar()
        self.n_txt.set("Nombre de divison: ")
        self.label_n = Label(self.fr3, textvariable=self.n_txt,justify=RIGHT, anchor="w", height=4, font=("Arial", 12))
        self.label_n.grid(sticky = E, row=5, column=0)

        self.slider_n = Scale(self.fr3, from_=1, to=10, orient=HORIZONTAL)
        self.slider_n.grid(sticky = W, row=5, column=1)

            # Labels des reusltats
                # Valeur Exacte = V.E
        self.ve_txt=StringVar()
        self.ve_txt.set("Valeur Exacte: ")
        self.label_ve = Label(self.fr3, textvariable=self.ve_txt,justify=RIGHT, anchor="w", height=4, font=("Arial", 12))
        self.label_ve.grid(sticky = E, row=6, column=0)

        self.res_ve_txt=StringVar()
        self.res_ve = Label(self.fr3,textvariable=self.res_ve_txt,justify=RIGHT, anchor="w", width=10,borderwidth=3,bg="yellow green")
        self.res_ve.grid(sticky = W, row=6, column=1)

                # Valeur Approché = V.A
        self.va_txt=StringVar()
        self.va_txt.set("Valeur Approché: ")
        self.label_va = Label(self.fr3, textvariable=self.va_txt,justify=RIGHT, anchor="w", height=4, font=("Arial", 12))
        self.label_va.grid(sticky = E, row=7, column=0)

        self.res_va_txt=StringVar()
        self.res_va = Label(self.fr3,textvariable=self.res_va_txt,justify=RIGHT, anchor="w", width=10,borderwidth=3,bg="yellow green")
        self.res_va.grid(sticky = W, row=7, column=1)

                # Erreur
        self.er_txt=StringVar()
        self.er_txt.set("Erreur: ")
        self.label_er = Label(self.fr3, textvariable=self.er_txt,justify=RIGHT, anchor="w", height=4, font=("Arial", 12))
        self.label_er.grid(sticky = E, row=8, column=0)

        self.res_e_txt=StringVar()
        self.res_e = Label(self.fr3,textvariable=self.res_e_txt,justify=RIGHT, anchor="w", width=10,borderwidth=3,bg="yellow green")
        self.res_e.grid(sticky = W, row=8, column=1)

            # Button
        self.button_2 = Button (self.fr3, width =15,text="Afficher",bg="yellow green", command=self.choice, font=("Arial", 12))
        self.button_2.grid(row=9,column=0,columnspan=3)
        self.fr3.grid(row=1,column=2,padx=10,pady=10)

        # Figure
        self.fig = Figure(figsize=(6,6))
        self.a = self.fig.add_subplot(111)
        self.a.set_title ("Graphe de f", fontsize=16)
        self.a.set_ylabel("y", fontsize=14)
        self.a.set_xlabel("x", fontsize=14)
        self.a.set_facecolor("white")

        self.canvas = FigureCanvasTkAgg(self.fig, master=self.fr2)
        self.canvas.get_tk_widget().pack()
        self.canvas.draw()
        self.x = symbols('x')

    # Plot + Calculation Function

    def plot (self):

        self.chbf.set(False)
        self.chbd.set(False)
        self.chbp.set(False)

        f= lambda x: eval(self.box.get().lower())
        ff= lambda x: self.box.get().lower()
        self.t=np.linspace(float(self.boxa.get()), float(self.boxb.get()), 1001)

        self.pp=np.vectorize(f)
        p= ff(self.t)

        D = diff(p, self.x, self.slider.get()) # Calcule du Dérivé
        d = lambda x: eval(str(diff(p, self.x, self.slider.get())))
        I = integrate(p, self.x) # Calcule du Primitive
        i = lambda x: eval(str(integrate(p, self.x)))

        self.d_vect = np.vectorize(d)
        self.i_vect = np.vectorize(i)

        self.a.cla()
        self.a.set_ylim([float(self.boxa.get()), float(self.boxb.get())])
        self.a.xaxis.set_ticks(np.arange(float(self.boxa.get()), float(self.boxb.get())+1, 1))
        self.a.yaxis.set_ticks(np.arange(float(self.boxa.get()), float(self.boxb.get())+1, 1))
        self.a.set_title ("Graphe de f", fontsize=16)
        self.a.set_ylabel("y=f(x)", fontsize=14)
        self.a.set_xlabel("x", fontsize=14)
        self.a.grid(True)

        self.res_txt.set(D) # Afichage du Dérivé dans la GUI
        self.resp_txt.set(I) # Afichage du Primitive dans la GUI
        self.canvas.draw()

    def afficher_F(self):

            if self.chbf.get():
                self.a.plot(self.t, self.pp(self.t),color='blue', label='F(X)') # Afficher F'(x)
                handles, self.labels = self.a.get_legend_handles_labels()
                self.a.legend()
                self.canvas.draw()

            else:
                handles, self.labels = self.a.get_legend_handles_labels()
                j=-1

                for i in self.labels :

                    j+=1

                    if i == "F(X)" :

                        del self.a.lines[j] # Supprimer F(x)
                        self.a.legend()
                        self.canvas.draw()

    def afficher_D(self):

            if self.chbd.get():

                self.a.plot(self.t, self.d_vect(self.t),color='green', label='Dérivé') # Afficher F'(x)
                handles, self.labels = self.a.get_legend_handles_labels()
                self.a.legend()
                self.canvas.draw()

            else :
                handles, self.labels = self.a.get_legend_handles_labels()
                j=-1

                for i in self.labels :

                    j+=1

                    if i == "Dérivé" :

                        del self.a.lines[j] # Supprimer F'(x)
                        self.a.legend()
                        self.canvas.draw()

    def afficher_P(self):

            if self.chbp.get():

                self.a.plot(self.t, self.i_vect(self.t),color='red', label="Primitive") # Afficher F-1(x)
                handles, self.labels = self.a.get_legend_handles_labels()
                self.a.legend()
                self.canvas.draw()

            else :
                handles, self.labels = self.a.get_legend_handles_labels()
                j=-1

                for i in self.labels :

                    j+=1

                    if i == "Primitive" :

                        del self.a.lines[j] # Supprimer F'(x)
                        self.a.legend()
                        self.canvas.draw()

    def affichier_Rect(self):

        a = int(self.boxinf.get())
        b = int(self.boxsup.get())
        n = self.slider_n.get()
        x = np.linspace(a, b, n+1)

        f= lambda x: eval(self.box.get().lower())
        fx=np.vectorize(f)

        R = RectangleG(a, b, n, fx)

        fig = plt.figure()

        ax = fig.add_subplot(111)

        I,r = quad(fx, a, b)
        R.Graph(fx)

        self.res_ve_txt.set("%12.4f"%I)
        self.res_va_txt.set("%12.4f"% (R.integrate(fx)))
        self.res_e_txt.set("%12.4f"% (I-R.integrate(fx)))

        plt.grid(True)
        plt.show()

    def afficher_Milieu(self):

        a = int(self.boxinf.get())
        b = int(self.boxsup.get())
        n = self.slider_n.get()
        x = np.linspace(a, b, n+1)

        f= lambda x: eval(self.box.get().lower())
        fx=np.vectorize(f)

        M = Milieu(a,b,n,fx)

        fig = plt.figure()

        ax = fig.add_subplot(111)

        I,r = quad(fx, a, b)
        M.Graph(fx)

        self.res_ve_txt.set("%12.4f"%I)
        self.res_va_txt.set("%12.4f"% (M.integrate(fx)))
        self.res_e_txt.set("%12.4f"% (I-M.integrate(fx)))

        plt.grid(True)
        plt.show()

    def afficher_Trapez(self):

        a = int(self.boxinf.get())
        b = int(self.boxsup.get())
        n = self.slider_n.get()
        x = np.linspace(a, b, n+1)

        f= lambda x: eval(self.box.get().lower())
        fx=np.vectorize(f)

        T = Trapezoidal(a, b, n, fx)

        fig = plt.figure()

        ax = fig.add_subplot(111)
        I,r = quad(fx, a, b)
        T.Graph(fx)

        self.res_ve_txt.set("%12.4f"%(I))
        self.res_va_txt.set("%12.4f"% (T.integrate(fx)))
        self.res_e_txt.set("%12.4f"% (I-T.integrate(fx)))

        plt.grid(True)
        plt.show()

    def afficher_Simpson(self):

        a = int(self.boxinf.get())
        b = int(self.boxsup.get())
        n = self.slider_n.get()
        x = np.linspace(a, b, n+1)

        f= lambda x: eval(self.box.get().lower())
        fx=np.vectorize(f)

        S = Simps(a, b, n, fx)

        fig = plt.figure()

        ax = fig.add_subplot(111)
        I,r = quad(fx, a, b)
        S.Graph(fx)

        self.res_ve_txt.set("%12.4f"%(I))
        self.res_va_txt.set("%12.4f"% (S.integrate(fx)))
        self.res_e_txt.set("%12.4f"% (I-S.integrate(fx)))

        plt.grid(True)
        plt.show()

    def choice(self):
        ch = self.meth.get()

        if ch == "Rectangle":
            self.affichier_Rect()

        elif ch == "Point Milieu":
            self.afficher_Milieu()

        elif ch == "Trapéze":
            self.afficher_Trapez()

        elif ch == "Simpson":
            self.afficher_Simpson()

        else:
            showwarning("Warning", "Vous avez oubliez un paramétre \nou bien vous n'aves pas selectioner une méthode")

if __name__ == '__main__':

    window= Tk()

    start= mclass(window)

    window.mainloop()

import numpy,from numpy,from scipy,from sympy