import math
import numpy as np

class MatrixFactory(object):

    @staticmethod
    def makeIdentity():
        return np.array([
            [1, 0, 0, 0],  #
            [0, 1, 0, 0],  #
            [0, 0, 1, 0],  #
            [0, 0, 0, 1],  #
        ])

    @staticmethod
    def makeTranslation(x, y, z):
        return np.array([
            [1, 0, 0, x],  #
            [0, 1, 0, y],  #
            [0, 0, 1, z],  #
            [0, 0, 0, 1],  #
        ])

    # rotation around x axis
    @staticmethod
    def makeRotationX(angle):
        c = math.cos(angle)
        s = math.sin(angle)
        return np.array([
            [1, 0, 0, 0],  #
            [0, c, -s, 0],  #
            [0, s, c, 0],  #
            [0, 0, 0, 1],  #
        ])

    # rotation around y axis
    @staticmethod
    def makeRotationY(angle):
        c = math.cos(angle)
        s = math.sin(angle)
        return np.array([
            [c, 0, s, 0],  #
            [0, 1, 0, 0],  #
            [-s, 0, c, 0],  #
            [0, 0, 0, 1],  #
        ])

    # rotation around z axis
    @staticmethod
    def makeRotationZ(angle):
        c = math.cos(angle)
        s = math.sin(angle)
        return np.array([
            [c, -s, 0, 0],  #
            [s, c, 0, 0],  #
            [0, 0, 1, 0],  #
            [0, 0, 0, 1],  #
        ])

    # translation along a specified (normalized) axis
    @staticmethod
    def makeTranslationAxisDistance(axis, distance):
        displacement = np.array(axis) * distance
        return np.array([
            [1, 0, 0, displacement[0]],  #
            [0, 1, 0, displacement[1]],  #
            [0, 0, 1, displacement[2]],  #
            [0, 0, 0, 1],  #
        ])

    # rotation around a specified (normalized) axis
    @staticmethod
    def makeRotationAxisAngle(axis, angle):
        c = math.cos(angle)
        s = math.sin(angle)
        t = 1 - c
        x = axis[0]
        y = axis[1]
        z = axis[2]
        tx = t * x
        ty = t * y
        return np.array([
            [tx * x + c, tx * y - s * z, tx * z + s * y, 0],  #
            [tx * y + s * z, ty * y + c, ty * z - s * x, 0],  #
            [tx * z - s * y, ty * z + s * x, t * z * z + c, 0],  #
            [0, 0, 0, 1],  #
        ])

    # make scale
    @staticmethod
    def makeScaleUniform(s):
        return np.array([
            [s, 0, 0, 0],  #
            [0, s, 0, 0],  #
            [0, 0, s, 0],  #
            [0, 0, 0, 1],  #
        ])

    @staticmethod
    def makePerspective(fov=60, aspect=1, near=0.1, far=1000):
        D2R = 3.1415926 / 180.0
        yScale = 1.0 / math.tan(D2R * fov / 2)
        xScale = yScale / aspect
        c = (far + near) / (near - far)
        d = 2 * far * near / (near - far)
        return np.array([
            [xScale, 0, 0, 0],  #
            [0, yScale, 0, 0],  #
            [0, 0, c, d],  #
            [0, 0, -1, 0],  #
        ])

    @staticmethod
    def makeOrthographic(left=-1, right=1, top=1, bottom=-1, far=1, near=-1):
        return np.array([
            [2 / (right - left), 0, 0, -(right + left) / (right - left)],  #
            [0, 2 / (top - bottom), 0, -(top + bottom) / (top - bottom)],  #
            [0, 0, -2 / (far - near), -(far + near) / (far - near)],  #
            [0, 0, 0, 1],  #
        ])

    @staticmethod
    def makeLookAt(position, target, up):

        forward = np.subtract(target, position)
        forward = np.divide(forward, np.linalg.norm(forward))

        right = np.cross(forward, up)
        right = np.divide(right, np.linalg.norm(right))

        up = np.cross(right, forward)
        up = np.divide(up, np.linalg.norm(up))

        return np.array([
            [right[0], up[0], -forward[0], position[0]],  #
            [right[1], up[1], -forward[1], position[1]],  #
            [right[2], up[2], -forward[2], position[2]],  #
            [0, 0, 0, 1],  #
        ])


import math
import numpy as np
from nltk.metrics.association import TOTAL
from sklearn import metrics
from matplotlib.mlab import entropy

class ExternalIndices:
    def __init__(self, true_labels, clust_labels):

        self.true_labels = true_labels - 1
        self.clust_labels = clust_labels - 1
        self.N = len(self.true_labels)
        self.n_clusters = np.max(clust_labels)

    def Ext_Jaccard(self):

        a = 0
        b = 0
        c = 0
        for j in range(len(self.true_labels)):
            indtrue = self.true_labels[j] == self.true_labels
            indclus = self.clust_labels[j] == self.clust_labels
            a = a + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:]))  # #pairs of j
            b = b + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:] == False))
            c = c + np.sum(np.logical_and(indtrue[j + 1:] == False, indclus[j + 1:]))

        return a / (a + b + c)

    def Ext_RandStatistic(self):

        a = 0
        d = 0
        for j in range(len(self.true_labels)):
            indtrue = self.true_labels[j] == self.true_labels
            indclus = self.clust_labels[j] == self.clust_labels
            a = a + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:]))  # #pairs of j
            d = d + np.sum(np.logical_and(indtrue[j + 1:] == False, indclus[j + 1:] == False))

        M = self.N * (self.N - 1) / 2

        return (a + d) / M

    def Ext_Folkes_Mallows(self):
        a = 0
        b = 0
        c = 0
        d = 0
        for j in range(len(self.true_labels)):
            indtrue = self.true_labels[j] == self.true_labels
            indclus = self.clust_labels[j] == self.clust_labels
            a = a + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:]))  # #pairs of j
            b = b + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:] == False))
            c = c + np.sum(np.logical_and(indtrue[j + 1:] == False, indclus[j + 1:]))
            d = d + np.sum(np.logical_and(indtrue[j + 1:] == False, indclus[j + 1:] == False))

        return a / np.sqrt((a + b) * (a + c))

    def Ext_F_Measure(self):
        a = 0
        b = 0
        c = 0
        d = 0
        for j in range(len(self.true_labels)):
            indtrue = self.true_labels[j] == self.true_labels
            indclus = self.clust_labels[j] == self.clust_labels
            a = a + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:]))  # #pairs of j
            b = b + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:] == False))
            c = c + np.sum(np.logical_and(indtrue[j + 1:] == False, indclus[j + 1:]))
            d = d + np.sum(np.logical_and(indtrue[j + 1:] == False, indclus[j + 1:] == False))

        return 2 * a / (2 * a + b + c)

    def Ext_Hubert_Gamma(self):
        a = 0
        b = 0
        c = 0
        d = 0
        for j in range(len(self.true_labels)):
            indtrue = self.true_labels[j] == self.true_labels
            indclus = self.clust_labels[j] == self.clust_labels
            a = a + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:]))  # #pairs of j
            b = b + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:] == False))
            c = c + np.sum(np.logical_and(indtrue[j + 1:] == False, indclus[j + 1:]))

        M = self.N * (self.N - 1) / 2

        return (M * a - (a + b) * (a + c)) / np.sqrt(((a + b) * M - (a + b) ** 2) * ((a + c) * M - (a + c) ** 2))

    def Ext_Kulczynski(self):
        a = 0
        b = 0
        c = 0
        for j in range(len(self.true_labels)):
            indtrue = self.true_labels[j] == self.true_labels
            indclus = self.clust_labels[j] == self.clust_labels
            a = a + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:]))  # #pairs of j
            b = b + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:] == False))
            c = c + np.sum(np.logical_and(indtrue[j + 1:] == False, indclus[j + 1:]))

        return (1 / 2) * ((a / (a + c)) + (a / (a + b)))

    def Ext_McNemar(self):
        a = 0
        b = 0
        c = 0
        d = 0
        for j in range(len(self.true_labels)):
            indtrue = self.true_labels[j] == self.true_labels
            indclus = self.clust_labels[j] == self.clust_labels
            a = a + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:]))  # #pairs of j
            b = b + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:] == False))
            c = c + np.sum(np.logical_and(indtrue[j + 1:] == False, indclus[j + 1:]))
            d = d + np.sum(np.logical_and(indtrue[j + 1:] == False, indclus[j + 1:] == False))

        return (d - c) / np.sqrt(d + c)

    def Ext_Phi_index(self):
        a = 0
        b = 0
        c = 0
        d = 0
        for j in range(len(self.true_labels)):
            indtrue = self.true_labels[j] == self.true_labels
            indclus = self.clust_labels[j] == self.clust_labels
            a = a + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:]))  # #pairs of j
            b = b + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:] == False))
            c = c + np.sum(np.logical_and(indtrue[j + 1:] == False, indclus[j + 1:]))
            d = d + np.sum(np.logical_and(indtrue[j + 1:] == False, indclus[j + 1:] == False))

        return (1 / 2) * ((a / (a + c)) + (a / (a + b)))

    def Ext_Rogers_Tanimoto(self):
        a = 0
        b = 0
        c = 0
        d = 0
        for j in range(len(self.true_labels)):
            indtrue = self.true_labels[j] == self.true_labels
            indclus = self.clust_labels[j] == self.clust_labels
            a = a + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:]))  # #pairs of j
            b = b + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:] == False))
            c = c + np.sum(np.logical_and(indtrue[j + 1:] == False, indclus[j + 1:]))
            d = d + np.sum(np.logical_and(indtrue[j + 1:] == False, indclus[j + 1:] == False))

        return (a + d) / (a + d + 2 * b + 2 * c)

    def Ext_Russel_Rao(self):
        a = 0
        for j in range(len(self.true_labels)):
            indtrue = self.true_labels[j] == self.true_labels
            indclus = self.clust_labels[j] == self.clust_labels
            a = a + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:]))  # #pairs of j

        M = self.N * (self.N - 1) / 2
        return a / M

    def Ext_Sokal_Sneath(self):
        a = 0
        b = 0
        c = 0
        for j in range(len(self.true_labels)):
            indtrue = self.true_labels[j] == self.true_labels
            indclus = self.clust_labels[j] == self.clust_labels
            a = a + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:]))  # #pairs of j
            b = b + np.sum(np.logical_and(indtrue[j + 1:], indclus[j + 1:] == False))
            c = c + np.sum(np.logical_and(indtrue[j + 1:] == False, indclus[j + 1:]))

        return a / (a + 2 * (b + c))

    def Ext_Adjusted_rand_score(self):
        return metrics.adjusted_rand_score(self.true_labels, self.clust_labels)

    def Ext_Adjusted_Mutual_Information(self):
        return metrics.adjusted_mutual_info_score(self.true_labels, self.clust_labels)

    def Ext_Normalized_Mutual_Information(self):
        return metrics.normalized_mutual_info_score(self.true_labels, self.clust_labels)

    def Ext_Mutual_Information(self):
        return metrics.mutual_info_score(self.true_labels, self.clust_labels)

    def Ext_Homogeneity_Score(self):
        return metrics.homogeneity_score(self.true_labels, self.clust_labels)

    def Ext_Completeness_Score(self):
        return metrics.completeness_score(self.true_labels, self.clust_labels)

    def Ext_V_Measure_Score(self):
        return metrics.v_measure_score(self.true_labels, self.clust_labels)

    def Ext_Purity(self):
        partition_purity = 0
        for i in range(self.n_clusters):
            elements = self.true_labels[self.clust_labels == i]
            clus_purity = np.max(np.bincount(elements.astype(int)))
            partition_purity = partition_purity + clus_purity

        return partition_purity / self.N

    def Ext_Conditional_Entropy_V_given_U(self):
        partition_entropy = 0

        for i in range(self.n_clusters):
            elements = self.true_labels[self.clust_labels == i]
            prob = np.bincount(elements.astype(int)) / len(elements)
            prob = np.delete(prob, np.where(prob == 0))
            clus_entropy = np.sum(prob * np.log2(prob))
            partition_entropy = partition_entropy + clus_entropy * len(elements)

        return partition_entropy / self.N

    def Ext_Accuracy1(self):

        clusmax = np.empty((0, 2))
        p = 0
        for i in np.unique(self.clust_labels).astype(int):
            elements = self.true_labels[self.clust_labels == i]
            clmax = np.max(np.bincount(elements.astype(int)))
            clargm = np.argmax(np.bincount(elements.astype(int)))
            if p > 0:
                if np.sum(clusmax[:, 0] == clargm) == 0:
                    clusmax = np.concatenate((clusmax, np.array([[clargm, clmax]])), axis=0)
                else:
                    if clusmax[clusmax[:, 0] == clargm, 1] < clmax:
                        clusmax[clusmax[:, 0] == clargm, :] = np.array([[clargm, clmax]])
            else:
                clusmax = np.concatenate((clusmax, np.array([[clargm, clmax]])), axis=0)
            p += 1

        return np.sum(clusmax[:, 1]) / len(self.clust_labels)


# Question: https://projecteuler.net/problem=116

import numpy as np

N = 50

tile_length = [2,3,4]

DP = np.zeros((len(tile_length), N+1), dtype = np.uint64)

for i, n in enumerate(tile_length):
    for j in range(n): # if we have fewer than n tiles, the only solution is placing nothing
        DP[i][j] = 1
    for j in range(n, N+1):
        # placing nothing: DP[i][j-1]
        # placing an additional n-tile: DP[i][j-n]
        DP[i][j] = DP[i][j-1] + DP[i][j-n]

# note that we also count placing no tiles in DP, so we'll need to substract 1 from DP[i][N]
print(DP[0][N] - 1 + DP[1][N] -1 + DP[2][N] - 1)

assert(DP[0][5] - 1 == 7)
assert(DP[1][5] - 1 == 3)
assert(DP[2][5] - 1 == 2)


__author__ = 'dixon'
from scipy.stats import norm
from math import *

def bsformula( callput, S0, K, r, T, sigma, q=0.):
    """
    :param callput: Indicates if the option is a Call or Put option
    :param S0: Stock price
    :param K: Strike price
    :param r: Risk-free rate
    :param T: Time to expiration
    :param sigma: Volatility
    :param q: Dividend rate
    :return: Value of the option, its Delta, its Vega
    """
    d1=(log(float(S0)/K)+((r-q)+sigma*sigma/2.)*T)/(sigma*sqrt(T))
    d2=d1-sigma*sqrt(T)
    if callput==1:
        optionValue=S0*exp(-q*T)*norm.cdf(d1)-K*exp(-r*T)*norm.cdf(d2)
        delta=norm.cdf(d1)
    elif callput==-1:
        optionValue=K*exp(-r*T)*norm.cdf(-d2)-S0*exp(-q*T)*norm.cdf(-d1)
        delta=-norm.cdf(-d1)

    vega=S0*sqrt(T)*norm.pdf(d1)
    return optionValue,delta,vega


import numpy as np
import matplotlib.pyplot as plt

def grafica_direcciones_pca(datos_x, datos_y):
    data = np.array([datos_x, datos_y])
    data[0,:] = data[0,:]-np.mean(data[0,:])
    data[1,:] = data[1,:]-np.mean(data[1,:])
    cov = np.cov(data)
    values, vectors = np.linalg.eig(cov)

    plt.figure()
    fig, ax = plt.subplots()
    for i in range(2):
        x = np.linspace(np.min(data[0,:]), np.max(data[0,:]), 100)
        y = -vectors[0,i]/(vectors[1,i])*x
        plt.plot(x,y)

    plt.scatter(data[0,:], data[1,:])
    plt.xlim(np.min(data[0,:]), np.max(data[0,:]))
    plt.ylim(np.min(data[1,:]), np.max(data[1,:]))
    plt.xlabel('X-avg(X)')
    plt.ylabel('Y-avg(Y)')
    ax.set_aspect('equal')
    plt.savefig("direcciones.png", bbox_inches='tight')

#datos_x =[ 0.49416816,  0.46465438, -1.73694346, -0.12428063,  2.06799947,
#        2.0545997 ,  2.48939343, -0.81445802, -1.53473612, -1.75149721,
#       -3.88090413, -0.98148244,  0.266375  , -1.48104676, -0.28795435,
#        0.15063483,  0.55152368,  1.53011185, -1.46317706,  0.1278654 ]
#datos_y = [ 1.82086909e+00, -1.66252722e+00, -1.01376736e-03,  1.21411624e+00,
#       -1.22597662e+00, -7.48611751e-01, -9.38910856e-01,  8.23553713e-02,
#        5.61304037e-01,  1.41525966e+00, -2.53389854e-01, -2.19081552e-01,
#       -1.47090740e-01,  3.45632163e-01, -9.88379478e-02,  1.43713407e+00,
#       -4.64200400e-01, -9.67044402e-01,  1.01327199e+00,  5.21229231e-01]

#grafica_direcciones_pca(datos_x, datos_y)


# This program manipulates distance vectors in the robot coordinate frame,
# as well as arrays of vectors.  Pay attention to format of arguments since
# some functions are not yet optimized to handle numpy [1x2] vectors directly.
# Further functions will be added for rotation of vectors in various coordinate frames.

# Import external libraries
import numpy as np
# from numpy import exp, abs, angle
import time

# Import internal programs
import L1_lidar as lidar

np.set_printoptions(precision=3)                    # after math operations, don't print long values

def getValid(scan):                                 # remove the rows which have invalid distances
    dist = scan[:, 0]                               # store just first column
    angles = scan[:, 1]                             # store just 2nd column
    valid = np.where(dist > 0.016)                  # find values 16mm
    myNums = dist[valid]                            # get valid distances
    myAng = angles[valid]                           # get corresponding valid angles
    output = np.vstack((myNums, myAng))             # recombine columns
    n = output.T                                    # transpose the matrix
    return n

def nearest(scan):                                  # find the nearest point in the scan
    dist = scan[:, 0]                               # store just first column
    column_mins = np.argmin(dist, axis=0)           # get index of min values along 0th axis (columns)
    row_index = column_mins                         # index of the smallest distance
    vec = scan[row_index, :]                        # return the distance and angle of the nearest object in scan
    return vec                                      # contains [r, alpha]

def polar2cart(r, alpha):                           # convert an individual vector to cartesian coordinates (in the robot frame)
    alpha = np.radians(alpha)                       # alpha*(np.pi/180) # convert to radians
    x = r * np.cos(alpha)                           # get x
    y = r * np.sin(alpha)                           # get y
    cart = np.round(np.array([x, y]), 3)            # vectorize and round
    return cart

def rotate(vec, theta):                             # describe a vector in global coordinates by rotating from body-fixed frame
    c, s = np.cos(theta), np.sin(theta)             # define cosines & sines
    R = np.array(((c, -s), (s, c)))                 # generate a rotation matrix
    vecGlobal = np.matmul(R, vec)                   # multiply the two matrices
    return vecGlobal

def sumVec(vec, loc):                               # add two vectors. (origin to robot, robot to obstacle)
    mySum = vec + loc                               # element-wise addition takes place
    return mySum                                    # return [x,y]

def getNearest():                                   # combine multiple functions into one.  Call to get nearest obstacle.
    scan = lidar.polarScan()                        # get a reading in meters and degrees
    valids = getValid(scan)                         # remove the bad readings
    vec = nearest(valids)                           # find the nearest
    return vec                                      # pass the closest valid vector [m, deg]

if __name__ == "__main__":
    while True:
        myVector = getNearest()                                 # call the function which utilizes several functions in this program
        print("\n The nearest object (m,deg):\n", myVector)     # print the result
        time.sleep(0.1)                                         # small delay


import numpy as np
from scipy.linalg import expm, sinm, cosm
from scipy import integrate
from numpy import sin, cos, conj, cumsum, real, zeros, pi, trapz, arange, vstack, hstack, meshgrid, sqrt, diag, einsum, newaxis, float32
from numpy.random import rand

class WaveSolver2D():
    def __init__(self
                ,Fs = 48000 # sampling frequency
                ,dur = 0.02 # duration of the simulation
                ,lx = 1 # Room Parameters
                ,ly = 1
                ,c0 = 340
                ,rho = 1.2041
                ,spatial_delta = 1e-3 # spatial grid
                ,damping = 1 # artificial damping factor. Maybe interesting to change during learning or estimation?
                ,modes = 10
                ):
        super(WaveSolver2D, self).__init__()

        # Basic Parameter Set
        # Sampling frequency

        T = 1/Fs

        Mux = modes## FTM - Parameters
        Muy = modes

        self.numT = round(dur / T)
        self.t = np.linspace(0, dur, num=self.numT, endpoint=True ) # time vector

        self.numXs = round(lx / spatial_delta)
        self.numYs = round(ly / spatial_delta)
        xs = np.linspace(0, lx, num=self.numXs, endpoint=True) # space vector
        ys = np.linspace(0, ly, num=self.numYs, endpoint=True) # space vector

        # Create index vector.
        # We have modes in 2 directions x,y but we want to count the eigenvalues
        # with one index 'mu' --> each 'mu' is a index tupel
        # index = zeros((Mux*Muy,2))
        # index[:,2] = repmat(1:Muy,1,Mux)
        # index[:,1] = repelem(1:Mux, Muy)

        xv, yv = meshgrid(arange(Mux), arange(Muy))
        mux = xv.flatten() + 1
        muy = yv.flatten() + 1
        index = vstack((yv.flatten(),xv.flatten()))

        Mu = Mux*Muy
        mu = arange(Mu)
        smu = zeros((Mu),dtype=complex)

        ## FTM - Eigenvalues
        # create wavenumbers
        lamX = mux*pi/lx
        lamY = muy*pi/ly

        # create eigenvalues
        smu[mu] = 1j*c0*sqrt(lamX[mu]**2 + lamY[mu]**2)

        # Now condsider complex conjugated eigenvalues
        smu = hstack((smu, conj(smu)))
        lamX = hstack((lamX.transpose(), lamX.transpose()))
        lamY = hstack((lamY.transpose(), lamY.transpose()))
        Mu = smu.size

        # Add the artificial damping
        smu = smu - damping

        ## FTM - scaling factor
        nx = -8*lamX**2/(rho*smu**2)*lx*ly
        ny = -8*lamY**2/(rho*smu**2)*lx*ly
        nc = 8/(rho*c0**2)*lx*ly

        nmu = nx+ny+nc

        ## FTM - Eigenfunctions
        self.K1 = lambda x,y: 4*cos(lamX*x)*cos(lamY*y)
        self.K2 = lambda x,y: 4*lamX/(smu*rho)*sin(lamX*x)*cos(lamY*y)
        self.K3 = lambda x,y: 4*lamY/(smu*rho)*cos(lamX*x)*sin(lamY*y)

        self.Ka1 = lambda x,y: -4*lamX/(smu*rho)*sin(lamX*x)*cos(lamY*y)
        self.Ka2 = lambda x,y: -4*lamY/(smu*rho)*cos(lamX*x)*sin(lamY*y)
        self.Ka3 = lambda x,y: 4*cos(lamX*x)*cos(lamY*y)

        # Explicity copy variables
        self.lx = lx
        self.ly = ly
        self.smu = smu
        self.nmu = nmu
        self.T = T
        self.Fs = Fs
        self.xs = xs
        self.ys = ys

        self.lamX = lamX
        self.lamY = lamY

    def create_impulse(self, xe_rel, ye_rel):
        # Explicity copy variables
        lx, ly = self.lx, self.ly
        smu = self.smu

        ## Input signal

        # Use a delta at exciation position on the string
        xe = xe_rel*lx
        ye = ye_rel*ly

        # impulse excitation at (xe,ye)
        fe_xy = self.Ka3(xe,ye)

        return fe_xy

    def create_random_initial(self):
        # Explicity copy variables
        lx, ly = self.lx, self.ly
        xs, ys = self.xs, self.ys
        smu = self.smu
        lamX, lamY = self.lamX, self.lamY

        fe_xy = np.zeros((1, smu.size))

        # Random excitation
        for mu in range(smu.size) :
            g = smu[mu]
            rx = (rand(1,xs.size)*2-1)
            ry = (rand(1,ys.size)*2-1)

            Kx = 4*cos(lamX[mu]*xs)
            Ky = cos(lamY[mu]*ys)

            funX = 4*cos(lamX[mu]*xs)*(rx)
            funY = cos(lamY[mu]*ys)*(ry)

            integ = trapz(xs,funX)*trapz(ys,funY)
            fe_xy[:,mu] = integ[0]

        return fe_xy

    def solve(self, fe_x):
        ## Copy internal variables
        T = self.T
        smu, nmu = self.smu, self.nmu
        lx, ly = self.lx, self.ly
        xs, ys = self.xs, self.ys

        ## Simulation - state equation

        ybar = zeros((smu.size, self.t.size),dtype=complex)

        # Matrix of eigenvalues, As: Frequency domain, Az: discrete-time domain
        As = diag(smu)
        Az = expm(As*T)

        # Input at t = 0 --> is realized by an initial value for ybar
        ybar[:,0] = fe_x

        for k in range(1,self.t.size) : #for k = 2:length(t)
            # Process state equation
            ybar[:,k] = Az@ybar[:,k-1]

        ## Simulation - output equation
        xo = 0.7*lx
        yo = 0.7*ly

        # take first entry (sound pressure)
        y = (self.K1(xo,yo)/nmu)@ybar
        y = real(y)

        ## Simulation - spatial domain
        K1_sp = zeros((xs.size, ys.size, nmu.size),dtype=complex) # Eigenfunctions for sound pressure
        K2_vx = zeros((xs.size, ys.size, nmu.size),dtype=complex) # Eigenfunctions for particle velocity in x-direction
        K3_vy = zeros((xs.size, ys.size, nmu.size),dtype=complex) # Eigenfunctions for particle velocity in y-direction

        y_sp = zeros((xs.size, ys.size, self.t.size),dtype=complex) # Output sound pressure
        y_vx = zeros((xs.size, ys.size, self.t.size),dtype=complex) # output particle velocity in x-direction
        y_vy = zeros((xs.size, ys.size, self.t.size),dtype=complex) # output particle velocity in y-direction

        for xi in range(xs.size) :
            for yi in range(ys.size) :
                K1_sp[xi,yi,:] = self.K1(xs[xi], ys[yi])/nmu
                K2_vx[xi,yi,:] = self.K2(xs[xi], ys[yi])/nmu
                K3_vy[xi,yi,:] = self.K3(xs[xi], ys[yi])/nmu

                y_sp[xi,yi,:] = K1_sp[xi,yi,:]@ybar
                y_vx[xi,yi,:] = K2_vx[xi,yi,:]@ybar
                y_vy[xi,yi,:] = K3_vy[xi,yi,:]@ybar

        y = float32(real(y))
        ybar = float32(real(ybar))
        y_sp = float32(real(y_sp))
        y_vx = float32(real(y_vx))
        y_vy = float32(real(y_vy))

        return y, ybar, y_sp, y_vx, y_vy


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

import SimpleITK as sitk
import numpy as np

import argparse
import math

def compute_mean( arr_one ):
    mean = 0
    all_voxels = arr_one.size

    for voxel in np.nditer(arr_one):
        mean += voxel

    mean /= float(all_voxels)

    return mean

def compute_std( arr_one ):
    variance = 0
    std = 0
    #=================== YOUR CODE HERE ==============================
    # Based on the example to compute the mean, compute the standard
    # deviation of the image intensities

    #=================================================================
    std = math.sqrt(variance)
    return std

def compute_mode( arr_one ):
    mode = 0

    # Investigate the role of histogram function
    h, bins = np.histogram( arr_one, bins=255, range=(0,arr_one.max()) )

    #=================== YOUR CODE HERE ==============================
    # Compute the mode by inspecting the image histogram computed in
    # the previous line. Investigate the role of the numpy function
    # histogram.

    #=================================================================
    return mode

def compute_volume_per_label( arr_one ):
    volumes = []
    max_label = arr_one.max()

    #Initialise in zero
    for i in range (max_label):
        volumes.append(0)
    #=================== YOUR CODE HERE ==============================
    # Count the number of voxels associated to each of the labels
    # in the image and return these numbers in the array volumes.
    # Do not compute the volume for the background (value=0).

    #=================================================================
    return volumes

def compute_mean_max_volume( arr_one, arr_two, val ):
    mean = 0
    count = 0

    for voxels, labels in np.nditer([arr_one,arr_two]):
    #=================== YOUR CODE HERE ====================

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

    return mean

def convert_to_binary_mask( arr_one ):
    new_arr = arr_one
    #=================== YOUR CODE HERE ==============================
    # Assign 1 to every voxel in the image that is different from 0.

    #=================================================================
    return new_arr

#************************* main section **********************************
print 'SimpleITK: Interaction with numpy'

# This lines of code allow to read arguments from the command line
parser = argparse.ArgumentParser()
parser.add_argument("-i", "--img", required=True, help="Input image")
parser.add_argument("-m", "--mask", required=True, help="Mask image")
parser.add_argument("-o", "--out", required=True, help="Output image")
args = parser.parse_args()

#1- Image reading
image=sitk.ReadImage(args.img)

#=================== YOUR CODE HERE ==============================
# Complete code here to read the mask image.
# Hint: The mask image name is stored in args.mask

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

#2- Converting a SimpleITK image object to an array
img_npy = sitk.GetArrayFromImage( image )
help( sitk.GetArrayFromImage )

#=================== YOUR CODE HERE ==============================
# Convert the mask image you read into a numpy array

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

#3- Compute image statistics - Complete code for std and mode
mean_val=compute_mean( img_npy )
std_val=compute_std( img_npy )
mode_val=compute_mode( img_npy )

#=================== YOUR CODE HERE ==============================
# Compute the volume for each label of the mask image by invoking
# compute_volume_per_label. Note: You should also implement this
# function. Save the result in vol

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

#=================== YOUR CODE HERE ==============================
# Find the label with the largest volume. Then, implement
# compute_mean_max_volume so that the mean of the region with the
# largest volume is computed.

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

#=================== IMPLEMENT FUNCTION ==========================
# Implement the function convert_to_binary_mask, to binarise the
# voxels of the mask image, i.e. assign a value of 1 to every
# voxel different from zero.
new_arr = convert_to_binary_mask( mask_npy )
#================================================================

help( sitk.GetImageFromArray )

#=================== YOUR CODE HERE ==============================
# Convert new_arr numpy array to a SimpleITK image and write to disk.
# Hint: The filename of the image to be saved is stored in args.out

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

#==================== Print out your results =====================
# Print values of mean, std, volumes per label and mean over a
# regio and image info of the new mask. Compare with others to
# validate your results


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Created on Sun Jan 23 12:37:59 2022

@author: jimmy
"""
# Beta destribution

import numpy as np
import scipy.stats as sts
import matplotlib.pyplot as plt
%matplotlib inline

a, b = 7, 3

beta_rv = sts.beta(a, b)
sample = beta_rv.rvs(50)

# plt.plot(sample)
sample[:10]

plt.hist(sample, density=True, bins = 20)

x = np.linspace(0,1,100)
pdf = beta_rv.pdf(x)
plt.plot(x, pdf)

plt.ylabel('histogram vs theoretical PDF')
plt.xlabel('$x$')

m = a / (a + b)
D = a*b / (a+b)**2 / (a+b+1)
sigma = np.sqrt(D)

m, sigma

# checking theorem

n = 5
all_samples_of_n = []
for i in range(1000):
    all_samples_of_n.append(np.random.choice(sample, n))
means = np.array([s.mean() for s in all_samples_of_n])
plt.hist(means, density=True, bins = 30)
plt.ylabel('PDF')
plt.xlabel('$mean$')

x = np.linspace(0,1,100)
norm_rv = sts.norm(loc=m, scale = sigma/2)
pdf = norm_rv.pdf(x)
plt.plot(x, pdf)

norm_rv_unscaled = sts.norm(loc=m, scale = sigma)
pdf = norm_rv_unscaled.pdf(x)
plt.plot(x, pdf)

plt.xlim(0.4,0.9)

print('Approximate value: ', round(m,4), round(means.mean(),4))

print('Beta distribution', round(sigma/2, 4),  round(np.std(means),4))

n = 20
all_samples_of_n = []
for i in range(1000):
    all_samples_of_n.append(np.random.choice(sample, n))
means = np.array([s.mean() for s in all_samples_of_n])
plt.hist(means, density=True, bins = 30)
plt.ylabel('PDF')
plt.xlabel('$mean$')

x = np.linspace(0,1,100)
norm_rv = sts.norm(loc=m, scale = sigma/4)
pdf = norm_rv.pdf(x)
plt.plot(x, pdf)

plt.xlim(0.6,0.95)

n = 50
all_samples_of_n = []
for i in range(1000):
    all_samples_of_n.append(np.random.choice(sample, n))
means = np.array([s.mean() for s in all_samples_of_n])
plt.hist(means, density=True, bins = 22)
plt.ylabel('PDF')
plt.xlabel('$mean$')

x = np.linspace(0,1,100)
norm_rv = sts.norm(loc=m, scale = sigma/7)
pdf = norm_rv.pdf(x)
plt.plot(x, pdf)
plt.xlim(0.6,0.8)


# ISLR Ch 9 by Carol Cui

import numpy as np
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
import seaborn as sns
from mlxtend.plotting import plot_decision_regions
from sklearn.preprocessing import StandardScaler
from sklearn.model_selection import train_test_split, GridSearchCV
from sklearn.linear_model import LogisticRegression
from sklearn.svm import SVC
from sklearn.decomposition import PCA
from sklearn.metrics import confusion_matrix

# ----------------------------------------------------------------------------
# Q4
np.random.seed(1)
n = 100
p = 2
y = np.concatenate((np.repeat(1,30), np.repeat(1,30), np.repeat(2,40)))
x = np.random.rand(n,p)
x[1:30,:]  = x[1:30,:]  + 2
x[31:60,:] = x[31:60,:] - 2
plt.scatter(x[:,0], x[:,1], c=y, cmap=mpl.cm.Paired)
plt.xlabel('x1')
plt.ylabel('x2')

xtrain, xtest, ytrain, ytest = train_test_split(x, y, train_size=0.5, random_state=2)

# SVC
svc = SVC(C=10, kernel='linear')
svc.fit(xtrain, ytrain)
print(confusion_matrix(ytrain, svc.predict(xtrain))) # training error = 8/50 = 16%
plot_decision_regions(X=xtrain, y=ytrain, clf=svc, legend=2)
plt.xlabel('x1')
plt.ylabel('x2')

# SVM poly
svmp = SVC(C=10, kernel='poly', degree=4)
svmp.fit(xtrain, ytrain)
print(confusion_matrix(ytrain, svmp.predict(xtrain))) # training error = 0%
plot_decision_regions(X=xtrain, y=ytrain, clf=svmp, legend=2)
plt.xlabel('x1')
plt.ylabel('x2')

# SVM radial
svmr = SVC(C=10, kernel='rbf', gamma=1.0)
svmr.fit(xtrain, ytrain)
print(confusion_matrix(ytrain, svmr.predict(xtrain))) # training error = 0%
plot_decision_regions(X=xtrain, y=ytrain, clf=svmr, legend=2)
plt.xlabel('x1')
plt.ylabel('x2')

# SVM radial and poly outperform SVC w.r.t. training error.

# test errors
print('SVC test error: %.2f' %(1-svc.score(xtest,ytest)))
print('SVM poly test error: %.2f' %(1-svmp.score(xtest,ytest)))
print('SVM radial test error: %.2f' %(1-svmr.score(xtest,ytest)))
# SVM poly and radial perform eqaully well on test data.

# ----------------------------------------------------------------------------
# Q5
# (a)
np.random.seed(1)
n = 500
p = 2

x1 = np.random.uniform(0, 1, n) - 0.5
x2 = np.random.uniform(0, 1, n) - 0.5
y  = 1*(x1**2-x2**2 > 0)
df = pd.DataFrame({'x1':x1, 'x2':x2, 'y':y})

# (b)
fig1 = sns.lmplot(x='x1', y='x2', data=df, fit_reg=False, hue='y', legend=False)

# (c)
glm1     = LogisticRegression()
glm1_fit = glm1.fit(df.iloc[:,0:2], df['y'])

# (d)
glm1_pred = glm1_fit.predict(df.iloc[:,0:2])
fig2, (ax1, ax2) = plt.subplots(1, 2)
ax1.scatter(df['x1'], df['x2'], c=glm1_pred, cmap=mpl.cm.Paired)
ax1.set_xlabel('x1')
ax1.set_ylabel('x2')
ax1.set_title('linear logit')

# (e)
df['x12'] = df['x1']**2
df['x22'] = df['x2']**2
glm2      = LogisticRegression()
glm2_fit  = glm2.fit(df.drop('y', axis=1), df['y'])

# (f)
glm2_pred = glm2_fit.predict(df.drop('y', axis=1))
ax2.scatter(df['x1'], df['x2'], c=glm2_pred, cmap=mpl.cm.Paired)
ax2.set_xlabel('x1')
ax2.set_ylabel('x2')
ax2.set_title('non-linear logit')

# (g)
svc = SVC(C=10, kernel='linear')
svc.fit(df.iloc[:,0:2], df['y'])

# (h)
svmp = SVC(C=10, kernel='poly', degree=2)
svmp.fit(df.iloc[:,0:2], df['y'])

# (i)
fig3   = plt.figure()
labels = ['SVC', 'SVM poly']
gs     = gridspec.GridSpec(1, 2)
for clf, lab, grd in zip([svc, svmp], labels, ([0,0], [0,1])):
    clf.fit(np.stack((x1, x2), axis=-1), y)
    ax   = plt.subplot(gs[grd[0], grd[1]])
    fig3 = plot_decision_regions(X=np.stack((x1, x2), axis=-1), y=y, clf=clf, legend=2)
    plt.title(lab)
plt.show()

# While SVC and linear logit are similar, SVM poly and non-linear logit are similar.

# ----------------------------------------------------------------------------
# Q6
# (a)
np.random.seed(1)
n = 100
p = 2
y = np.concatenate((np.repeat(1,30), np.repeat(2,70)))
x = np.random.rand(n,p)
x[y==1,:] = x[y==1,:] + 0.9
df = pd.DataFrame({'x1':x[:,0], 'x2':x[:,1], 'y':y})

sns.lmplot(x='x1', y='x2', data=df, fit_reg=False, hue='y', legend=False)

# (b)
tune_param = [{'C': [0.1, 1, 10, 100, 1000]}]

tune_out = GridSearchCV(SVC(kernel='linear'), tune_param, cv=10, scoring='accuracy', return_train_score=True)
tune_out.fit(x, y)
tune_out.best_params_

print('mean CV error: %s' %(1-tune_out.cv_results_['mean_test_score']))
# best cost: 1
# CV error can go up as cost rises.

print('mean train error: %s' %(1-tune_out.cv_results_['mean_train_score']))
# best cost: 1000
# Training error never rises in cost.

# (c)
np.random.seed(10)
ytest = np.concatenate((np.repeat(1,30), np.repeat(2,70)))
xtest = np.random.rand(n,p)
xtest[ytest==1,:] = xtest[ytest==1,:] + 0.9
dftest = pd.DataFrame({'x1':xtest[:,0], 'x2':xtest[:,1], 'y':ytest})
sns.lmplot(x='x1', y='x2', data=dftest, fit_reg=False, hue='y', legend=False)

test_err = np.zeros(5)
for i in range(0,5):
    svc = SVC(C=tune_param[0]['C'][i], kernel='linear')
    svc.fit(x, y)
    test_err[i] = 1-svc.score(xtest,ytest)

print('test error: %s' %test_err)
# best cost: 1
# Test error selection agrees with CV selection, not training error selection.

# (d) The claim at the end of Section 9.6.1 is true.

# ----------------------------------------------------------------------------
# Q7
Auto = pd.read_csv('C:\\Users\\Carol\\Desktop\\Auto.csv', na_values='?').dropna()

# (a)
Auto['mpg01'] = np.where(Auto['mpg'] > np.median(Auto['mpg']), 1, 0)
Auto          = Auto.drop(['mpg', 'name'], axis=1)
# scale continuous variables
var_no_scale  = ['cylinders', 'year', 'origin', 'mpg01']
var_to_scale  = ['displacement', 'horsepower', 'weight', 'acceleration']
scaled_var    = StandardScaler().fit_transform(Auto[var_to_scale])
temp1         = pd.DataFrame(scaled_var, columns=var_to_scale)
temp2         = Auto[var_no_scale].reset_index(drop=True)
df            = pd.concat([temp1, temp2], axis=1)
x             = df.iloc[:,:-1]
y             = df['mpg01']

# (b)
tune_param = [{'C': [0.01, 0.1, 1, 10]}]
tune_out   = GridSearchCV(SVC(kernel='linear'), tune_param, cv=5, scoring='accuracy', n_jobs=-1)
tune_out.fit(x, y)
best_svc   = tune_out.best_estimator_
print('best cost for SVC %s' %tune_out.best_params_) # best C=10

# (c)
tune_param = [{'C': [0.01, 0.1, 1, 10], 'degree': [2, 3, 4]}]
tune_out   = GridSearchCV(SVC(kernel='poly'), tune_param, cv=5, scoring='accuracy', n_jobs=-1)
tune_out.fit(x, y)
best_svmp  = tune_out.best_estimator_
print('best cost for SVM poly %s' %tune_out.best_params_) # best C=10, degree=2

tune_param = [{'C': [0.01, 0.1, 1, 10], 'gamma': [0.5, 1, 2]}]
tune_out   = GridSearchCV(SVC(kernel='rbf'), tune_param, cv=5, scoring='accuracy', n_jobs=-1)
tune_out.fit(x, y)
best_svmr  = tune_out.best_estimator_
print('best cost for SVM poly %s' %tune_out.best_params_) # best C=1, gamma=0.5

# (d)
pca      = PCA(n_components=2)
xreduced = pca.fit_transform(x)

model1 = SVC(C=10, kernel='linear')
clf1   = model1.fit(xreduced, y)

model2 = SVC(C=10, kernel='poly', degree=2)
clf2   = model2.fit(xreduced, y)

model3 = SVC(C=1, kernel='rbf', gamma=0.5)
clf3   = model3.fit(xreduced, y)

fig    = plt.figure()
fig.suptitle('decison surface using PCA transformed/projected features')
labels = ['SVC', 'SVM poly', 'SVM radial']
gs     = gridspec.GridSpec(3, 1)
for clf, lab, grd in zip([clf1, clf2, clf3], labels, ([0,0], [1,0], [2,0])):
    clf.fit(np.stack((xreduced[:,0], xreduced[:,1]), axis=-1), np.array(y))
    ax  = plt.subplot(gs[grd[0], grd[1]])
    fig = plot_decision_regions(X=np.stack((xreduced[:,0], xreduced[:,1]), axis=-1), y=np.array(y), clf=clf, legend=2)
    plt.title(lab)
plt.show()

err_name = ['SVC', 'SVM poly', 'SVM radial']
err_rate = [1-best_svc.score(x, y), 1-best_svmp.score(x, y), 1-best_svmr.score(x, y)]
print(pd.DataFrame({'training error rate': err_rate}, index=err_name))

# SVM radial fits the training data best.

# ----------------------------------------------------------------------------
# Q8
# (a)
OJ = pd.read_csv('C:\\Users\\Carol\\Desktop\\OJ.csv').drop('Unnamed: 0', axis=1)
OJ['Purchase'] = pd.factorize(OJ.Purchase)[0]
OJ['Store7']   = OJ.Store7.map({'No':0, 'Yes':1})

x = OJ.drop(['Purchase'], axis=1)
y = OJ['Purchase']
xtrain, xtest, ytrain, ytest = train_test_split(x, y, train_size=800, random_state=1)

# scale continuous variables
x_no_scale    = ['StoreID', 'SpecialCH', 'SpecialMM', 'Store7', 'STORE']
x_to_scale    = xtrain[xtrain.columns.difference(x_no_scale)]
scaler        = StandardScaler().fit(x_to_scale)
temp1         = pd.DataFrame(scaler.transform(x_to_scale), columns=xtrain.columns.difference(x_no_scale))
temp2         = xtrain[x_no_scale].reset_index(drop=True)
scaled_xtrain = pd.concat([temp1, temp2], axis=1)

x_to_scale    = xtest[xtest.columns.difference(x_no_scale)]
temp1         = pd.DataFrame(scaler.transform(x_to_scale), columns=xtest.columns.difference(x_no_scale))
temp2         = xtest[x_no_scale].reset_index(drop=True)
scaled_xtest  = pd.concat([temp1, temp2], axis=1)

# (b)
svc = SVC(C=0.01, kernel='linear')
svc.fit(scaled_xtrain, ytrain)

# (c)
svc_train_err = 1-svc.score(scaled_xtrain, ytrain)
svc_test_err  = 1-svc.score(scaled_xtest, ytest)
print('SVC training error: %.2f | SVC test error: %.2f' %(svc_train_err, svc_test_err))

# (d) & (e)
tune_param = [{'C': [0.01, 0.1, 1, 10]}]

tune_out = GridSearchCV(SVC(kernel='linear'), tune_param, cv=10, scoring='accuracy')
tune_out.fit(scaled_xtrain, ytrain)
best_svc = tune_out.best_estimator_

best_svc_train_err = 1-best_svc.score(scaled_xtrain, ytrain)
best_svc_test_err  = 1-best_svc.score(scaled_xtest, ytest)
print('best SVC training error: %.2f | best SVC test error: %.2f' %(best_svc_train_err, best_svc_test_err))

# (f)
svmr = SVC(C=0.01, kernel='rbf', gamma='auto')
svmr.fit(scaled_xtrain, ytrain)

svmr_train_err = 1-svmr.score(scaled_xtrain, ytrain)
svmr_test_err  = 1-svmr.score(scaled_xtest, ytest)
print('SVM radial training error: %.2f | SVM radial test error: %.2f' %(svmr_train_err, svmr_test_err))

tune_out   = GridSearchCV(SVC(kernel='rbf', gamma='auto'), tune_param, cv=10, scoring='accuracy')
tune_out.fit(scaled_xtrain, ytrain)
best_svmr  = tune_out.best_estimator_

best_svmr_train_err = 1-best_svmr.score(scaled_xtrain, ytrain)
best_svmr_test_err  = 1-best_svmr.score(scaled_xtest, ytest)
print('training error: %.2f | test error: %.2f' %(best_svmr_train_err, best_svmr_test_err))

# (g)
svmp = SVC(C=0.01, kernel='poly', degree=2)
svmp.fit(scaled_xtrain, ytrain)

svmp_train_err = 1-svmp.score(scaled_xtrain, ytrain)
svmp_test_err  = 1-svmp.score(scaled_xtest, ytest)
print('SVM poly training error: %.2f | SVM poly test error: %.2f' %(svmp_train_err, svmp_test_err))

tune_out   = GridSearchCV(SVC(kernel='poly', degree=2), tune_param, cv=10, scoring='accuracy')
tune_out.fit(scaled_xtrain, ytrain)
best_svmp  = tune_out.best_estimator_

best_svmp_train_err = 1-best_svmp.score(scaled_xtrain, ytrain)
best_svmp_test_err  = 1-best_svmp.score(scaled_xtest, ytest)
print('training error: %.2f | test error: %.2f' %(best_svmp_train_err, best_svmp_test_err))

# (h)
x_label = np.arange(6)
plt.bar(x_label, [svc_test_err, svmr_test_err, svmp_test_err, best_svc_test_err, best_svmr_test_err, best_svmp_test_err])
plt.xticks(x_label, ('linear', 'radial', 'poly', 'best linear', 'best radial', 'best poly'))
plt.ylabel('test error')
# SVC performs the best.


import csv
import numpy as np
import matplotlib.pyplot as plt
import time
import sys
import warnings

if not sys.warnoptions:
    warnings.simplefilter("ignore")

path_X = sys.argv[1];
path_Y = sys.argv[2];
tau = float(sys.argv[3]);

# Read the CSV files to create X_weighted and Y_weighted

read_1 = [];
with open(path_X) as csvfile:
    reader = csv.reader(csvfile, quoting=csv.QUOTE_NONNUMERIC) # change contents to floats
    for row in reader: # each row is a list
        read_1.append(row)

X_rawest = np.asarray(read_1)
mu = np.mean(X_rawest);
sigma = np.std(X_rawest);
X_raw = (X_rawest - mu)/sigma;
X_weighted = np.c_[ np.ones((X_raw.shape[0],1)), X_raw]

read_2 = [];
with open(path_Y) as csvfile:
    reader = csv.reader(csvfile, quoting=csv.QUOTE_NONNUMERIC) # change contents to floats
    for row in reader: # each row is a list
        read_2.append(row)

Y_weighted = np.asarray(read_2)

m = Y_weighted.shape[0];

theta_linear = np.matmul(np.linalg.pinv(np.matmul(X_weighted.T,X_weighted)),np.matmul(X_weighted.T,Y_weighted))

fig1,ax1 = plt.subplots()
plt.plot(X_rawest,Y_weighted,'ro');
x1 = np.arange(np.min(X_rawest), np.max(X_rawest), 0.01)
theta_final = np.zeros((2,1));
theta_final[0] = theta_linear[0]-mu*theta_linear[1]/sigma;
theta_final[1] = theta_linear[1]/sigma;
y1 = theta_final[0] + theta_final[1]*x1;
plt.plot(x1,y1);
plt.xlabel('Inputs')
plt.ylabel('Outputs')
plt.title('Unweighted Linear Regression')
plt.show(block=False);
input("Press Enter to go to part (b)")
plt.close(fig1)

def normal_gradient(X,Y,tau,i):
    theta_w = np.zeros((2,1));
    W = np.zeros((m,m));
    for j in range(m):
        W[j,j] = np.exp(-1.0* np.square(X[i,1] - X[j,1])/(2*tau*tau));

    inv_mat = np.linalg.pinv(np.matmul(np.matmul(X.T,W.T + W),X));
    other_mat = np.matmul(np.matmul(Y.T,W.T + W),X);
    theta_w = np.matmul(inv_mat,other_mat.T);
    return np.matmul(X[i],theta_w)

def weighted_reg(X,Y,tau):
  Y_pred = np.zeros(Y.shape);
  for i in range (Y.shape[0]):
    Y_pred[i] = normal_gradient(X, Y, tau, i);

  X_total = np.c_[X_rawest, Y_pred];
  c = np.rec.fromarrays([X_rawest, Y, Y_pred]);
  c.sort(axis=0);
  fig,ax = plt.subplots()
  plt.plot(c.f0,c.f1,'ro');
  plt.plot(c.f0, c.f2, 'b--');
  plt.xlabel('Inputs')
  plt.ylabel('Outputs')
  plt.title('Weighted Linear Regression (tau = %.2f)' %tau)
  plt.show();

weighted_reg(X_weighted,Y_weighted,tau);

for i in [0.1,0.3,2,10]:
  weighted_reg(X_weighted,Y_weighted,i);


import numpy as np
from scipy.special.orthogonal import p_roots
import scipy.sparse as sps
from scipy.special import j_roots, eval_legendre

def gll_points(n):
    """GLL points and weights

    :param n: Number of points
    :returns: (x, w)
    :rtype:

    """

    assert n>=2

    if n==2:
        x = np.array([-1.0, 1.0])
        w = np.array([ 1.0, 1.0])
        return x, w

    # See Nodal Discontinuous Galerkin Methods Appendix A for x and
    # the Mathworld page on Lobatto Quadrature for w
    x = j_roots(n-2, 1, 1)[0]
    L = eval_legendre(n-1, x)
    w1 = 2.0/(n*(n-1))
    w  = 2.0/(n*(n-1)*L*L)

    x = np.hstack([-1.0, x, 1.0])
    w = np.hstack([w1, w, w1])

    return x, w

def eval_lagrange_d0(xnodes, xref):
    """Stable lagrange eval on interval ref

    :param xnodes: Lagrange nodes
    :param xref: Ref locations to eval
    :returns: phi at ref points for each basis function
    :rtype: np.ndarray: len(nxnodes) x len(xref)

    """

    nxnodes = len(xnodes)
    nx      = len(xref)

    phi1d = np.ones((nxnodes,nx),dtype=float)
    coeff = np.zeros((nxnodes,))
    for i in range(0,nxnodes):

        # compute (xi-x1)(xi-x2)...(xi-xn) for each x
        coeff[i]=1.0
        for j in range(0,nxnodes):
            if i!=j:
                coeff[i] *= xnodes[i] - xnodes[j]

        for k in range(0,nx):
            for j in range(0,nxnodes):
                if i!=j:
                    phi1d[i,k] *= xref[k] - xnodes[j]
            phi1d[i,k] *= 1.0/coeff[i]

    return phi1d

def eval_lagrange_d1(xnodes, xref):
    """Stable lagrange derivative eval on interval ref

    :param xnodes: Lagrange nodes
    :param xref: Ref locations to eval
    :returns: phi at ref points for each basis function
    :rtype: np.ndarray: len(nxnodes) x len(xref)

    """

    nxnodes = len(xnodes)
    nx      = len(xref)

    dphi1d = np.zeros((nxnodes,nx),dtype=float)
    coeff  = np.zeros((nxnodes,))
    for i in range(0,nxnodes):

        # compute (xi-x1)(xi-x2)...(xi-xn) for each x
        coeff[i]=1.0
        for j in range(0,nxnodes):
            if i!=j:
                coeff[i] *= xnodes[i] - xnodes[j]

        # comput d l_i at xg
        for k in range(0,nx):
            for j in range(0,nxnodes):
                if i!=j:
                    addon = 1.0
                    for l in range(0,nxnodes):
                        if (l!=j) and (l!=i):
                            addon *= xref[k]-xnodes[l]
                    dphi1d[i,k] += addon
            dphi1d[i,k] *= 1.0/coeff[i]

    return dphi1d

class Interval(object):

    interval = (-1.0, 1.0)
    h        = 2.0

    def calc_jacb(self, nodes):
        do_ravel = nodes.ndim==1
        if do_ravel:
            nodes = nodes.reshape((1,-1))

        jacb = (nodes[:,1]-nodes[:,0])/self.h
        assert np.all(jacb!=0.0)

        if do_ravel: return jacb.ravel()
        return jacb

    def calc_jacb_det(self, jacb):
        return jacb

    def calc_jacb_inv(self, jacb):
        return 1.0/jacb

    def calc_jacb_inv_det(self, jacb):
        return 1.0/jacb

    def get_quadrature(self, n):
        return p_roots(n)

    def ref_to_phys(self, nodes, ref):
        do_ravel = nodes.ndim==1
        if do_ravel:
            nodes = nodes.reshape((1,-1))

        a = nodes[:,0].reshape((-1,1))
        b = (nodes[:,1]-nodes[:,0])
        b = b.reshape((-1,1))
        phys = a+b*(ref+1)/self.h

        if do_ravel: return phys.ravel()
        return phys

    def phys_to_ref(self, nodes, phys):
        pass

class SEMhat(object):

    def __init__(self, order):

        n = order+1

        # GLL quadrature points
        xgll, wgll = gll_points(order+1)
        self.xgll, self.wgll = xgll, wgll

        # Mass matrix
        Bh = sps.dia_matrix((wgll, 0), shape=(n, n))
        self.Bh = Bh

        # Derivative evaluation matrix
        Dh = eval_lagrange_d1(xgll, xgll).T
        self.Dh = Dh

        # Diffusion operator
        # Ah = Dh'*Bh*Dh
        Ah = Dh.T.dot(Bh.dot(Dh))
        self.Ah = Ah

        # Convection operator
        Ch = Bh.dot(Dh)
        self.Ch = Ch

    def interp_mat(self, x):
        """Interpolation matrix from self.xgll to x

        :param x: Points to interpolate to
        :returns: Interpolation matrix
        :rtype: np.ndarray

        """

        J = eval_lagrange_d0(self.xgll, x)
        return J.T


import numpy as np
import matplotlib.pyplot as plt
from matplotlib import cm
from mpl_toolkits.mplot3d import Axes3D
# import tensorflow as tf
import tensorflow_probability as tfp

mean_range = np.arange(-2.5, 2.5, 0.1)
sigma_range = np.arange(0.1, 2, 0.1)
mesh_mean, mesh_sigma = np.meshgrid(mean_range, sigma_range)
L=np.zeros_like(mesh_mean)
for i, row in enumerate(mesh_mean):
    for j, _ in enumerate(row):
        dist = tfp.distributions.MultivariateNormalDiag(loc=[mesh_mean[i, j]], scale_diag=[mesh_sigma[i, j]])
        dist = tfp.distributions.TransformedDistribution(distribution=dist,
                                                         bijector=tfp.bijectors.Tanh(),
                                                         name='TanhMultivariateNormalDiag')
        neg_log_prob_a_t = -dist.log_prob([0.]).numpy()
        L[i, j] = neg_log_prob_a_t

L = np.clip(L, -10., 2.)
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
surf = ax.plot_surface(mesh_mean, mesh_sigma, L, linewidth=0, cmap=cm.coolwarm)
ax.set_zlim3d(top=2.)
plt.xlabel("Mean")
plt.ylabel("Stdev")
plt.show()

fig = plt.figure()
lgnd = []
mean_range = np.arange(0, 1.5, 0.2)
sigma_range = np.arange(0.1, 2.5, 0.1)
minvals = []
minargs = []
for mean in mean_range:
    lgnd.append("{:0.3f}".format(mean))
    L = np.zeros_like(sigma_range)
    for j, sigma in enumerate(sigma_range):
        dist = tfp.distributions.MultivariateNormalDiag(loc=[mean], scale_diag=[sigma])
        dist = tfp.distributions.TransformedDistribution(distribution=dist,
                                                         bijector=tfp.bijectors.Tanh(),
                                                         name='TanhMultivariateNormalDiag')
        neg_log_prob_a_t = -dist.log_prob([0.]).numpy()
        L[j] = neg_log_prob_a_t
    plt.plot(sigma_range, L)
    minargs.append(sigma_range[np.argmin(L)])
    minvals.append(np.amin(L))
plt.legend(lgnd)
plt.ylim([-2., 3.])
plt.xlabel("Stdev")
plt.ylabel("-log prob Gaussian(0.)")
plt.title("-log prob Gaussian(0.) as the function of \n Mean and Std of the probability distribution")
plt.plot(minargs, minvals)
plt.show()

fig = plt.figure()
sigma_range = np.arange(0.1, 1.5, 0.2)
sample_range = np.arange(-2., 2., 0.01)
lgnd = []
for sigma in sigma_range:
    lgnd.append("σ={:0.3f}".format(sigma))
    dist = tfp.distributions.MultivariateNormalDiag(loc=[0.], scale_diag=[sigma])
    dist = tfp.distributions.TransformedDistribution(distribution=dist,
                                                     bijector=tfp.bijectors.Tanh(),
                                                     name='TanhMultivariateNormalDiag')
    pdf = np.zeros_like(sample_range)
    for i, sample in enumerate(sample_range):
        pdf[i] = dist.prob([sample]).numpy()
    plt.plot(sample_range, pdf)

plt.legend(lgnd)
plt.title("Probability density function for tanh gaussian distribution")
plt.show()


r"""
Solve Poisson equation in 1D with possibly inhomogeneous Dirichlet bcs

    \nabla^2 u = f,

The equation to solve is

     (\nabla^2 u, v) = (f, v)

"""
import sys
import os
from sympy import symbols, sin, pi
import numpy as np
from shenfun import inner, div, grad, TestFunction, TrialFunction, \
    Array, Function, FunctionSpace, dx

assert len(sys.argv) == 3, 'Call with two command-line arguments'
assert sys.argv[-1].lower() in ('legendre', 'chebyshev', 'jacobi')
assert isinstance(int(sys.argv[-2]), int)

# Get family from args
family = sys.argv[-1].lower()

# Use sympy to compute a rhs, given an analytical solution
domain = (-1, 1)
a = 1.
b = -1.
if family == 'jacobi':
    a = 0
    b = 0

x = symbols("x", real=True)
d = 2/(domain[1]-domain[0])
x_map = -1+(x-domain[0])*d
ue = sin(4*pi*x_map)*(x_map-1)*(x_map+1) + a*(1-x_map)/2 + b*(1+x_map)/2
fe = ue.diff(x, 2)

# Size of discretization
N = int(sys.argv[-2])

SD = FunctionSpace(N, family=family, bc=(a, b), domain=domain, scaled=False)
u = TrialFunction(SD)
v = TestFunction(SD)

# Get f on quad points
fj = Array(SD, buffer=fe)

# Compute right hand side of Poisson equation
f_hat = Function(SD)
f_hat = inner(v, fj, output_array=f_hat)

# Get left hand side of Poisson equation
A = inner(v, div(grad(u)))

u_hat = Function(SD)
u_hat = A.solve(f_hat, u_hat)
uj = u_hat.backward()
uh = uj.forward()

# Compare with analytical solution
ua = Array(SD, buffer=ue)
print("Error=%2.16e" %(np.sqrt(dx((uj-ua)**2))))
assert np.allclose(uj, ua)


import numpy as np
from numba import jit

# Reference: https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm

@jit(nopython=True, nogil=True, cache=True, fastmath=True)
def _make_line_low(x0: int, y0: int, x1: int, y1: int):
    dx = x1 - x0
    dy = y1 - y0
    my = max(y0, y1)
    yi = 1
    if dy < 0:
        yi = -1
        dy = -dy
    D = 2 * dy - dx
    y = y0
    n = dx + 1
    line = np.zeros((n, 2), dtype=np.int32)
    for idx, x in enumerate(range(x0, x1)):
        line[idx] = [y, x]
        if D > 0:
            y += yi
            if y > my:
                y = my
            D += 2 * (dy - dx)
        else:
            D += 2 * dy
    line[-1] = [y1, x1]
    return line

@jit(nopython=True, nogil=True, cache=True, fastmath=True)
def _make_line_high(x0: int, y0: int, x1: int, y1: int):
    dx = x1 - x0
    dy = y1 - y0
    mx = max(x0, x1)
    xi = 1
    if dx < 0:
        xi = -1
        dx = -dx
    D = 2 * dx - dy
    x = x0
    n = dy + 1
    line = np.zeros((n, 2), dtype=np.int32)
    for idx, y in enumerate(range(y0, y1)):
        line[idx] = [y, x]
        if D > 0:
            x += xi
            if x > mx:
                x = mx
            D += 2 * (dx - dy)
        else:
            D += 2 * dx
    line[-1] = [y1, x1]
    return line

@jit(nopython=True, nogil=True, cache=True, fastmath=True)
def make_line(x0: int, y0: int, x1: int, y1: int) -> np.array:
    """
    Return a rasterised line between pairs of coordinates on a 2d integer grid according to the Bresenham algorithm.

    Includes both start and end coordinates. Guaranteed to stay within the bounds set by endpoints.

    Returns a numpy array of (y,x) line coordinates with shape (n, 2) where n is the number of points in the line.

    :param x0: start x coordinate
    :param y0: start y coordinate
    :param x1: end x coordinate
    :param y1: end y coordinate
    :return: numpy array of (y,x) line coordinates
    """
    if abs(y1 - y0) < abs(x1 - x0):
        if x0 > x1:
            return _make_line_low(x1, y1, x0, y0)
        else:
            return _make_line_low(x0, y0, x1, y1)
    else:
        if y0 > y1:
            return _make_line_high(x1, y1, x0, y0)
        else:
            return _make_line_high(x0, y0, x1, y1)


#Simpson's 1/3 Rule

"""
Name - Suryabrata Das

Sem: V

College_Roll_NO: 703

Paper-code: CMSA DSE-IB

Registration No: A01-1112-117-003-2018

Examination roll_no: 2021151264

Subject: Numerical Methods (DSE-I)

"""

import numpy as np
def func(x):
    return np.exp((-x/2))

def main():
    a = 1 #lower limit
    b = 2 #upper limit
    n = 4 #No. of steps
    s = 2
    h = ((b-a)/n)/s #interval
    i = a
    res = 0
    while True:
        res = res +((h*(func(i) + (4*func(i+h)) + func(i+(2*h))))/3)
        i = i+(h*s)
        if i == b:
            break
    print("Integration value: ",res)
if __name__ == '__main__':
    main()


import numpy as np
import jax.numpy as jnp
import jax

def get_point_orbit_2d_square(L, rotation, reflection):
    ''' This function generates the group of point symmetries in a two-dimensional square lattice.

    Arguments:
        * ``L``: Linear dimension of the lattice.
        * ``rotation``: Boolean to indicate whether rotations are to be included
        * ``reflection``: Boolean to indicate whether reflections are to be included

    Returns:
        A three-dimensional ``jax.numpy.array``, where the first dimension corresponds to the different
        symmetry operations and the following two dimensions correspond to the corresponding permuation matrix.
    '''

    trafos = []

    idx = np.arange(L * L).reshape((L, L))

    for _ in range(2 if reflection else 1):
        for _ in range(4 if rotation else 1):
            trafos.append(idx)
            idx = np.array(list(zip(*idx[::-1])))  # rotation
        idx = np.transpose(idx)  # reflection

    orbit = []

    idx = np.arange(L * L)

    for t in trafos:

        o = np.zeros((L * L, L * L), dtype=np.int32)

        o[idx, t.ravel()] = 1

        orbit.append(o)

    orbit = jnp.array(orbit)

    return orbit

def get_translation_orbit_2d_square(L, translation):
    ''' This function generates the group of translations in a two-dimensional square lattice.

    Arguments:
        * ``L``: Linear dimension of the lattice.
        * ``translation``: Boolean to indicate whether translations are to be included

    Returns:
        A three-dimensional ``jax.numpy.array``, where the first dimension corresponds to the different
        translations and the following two dimensions correspond to the corresponding permuation matrix.
    '''

    idx = np.arange(L**2, dtype=np.int32).reshape((L, L))

    trafos = []

    for lx in range(L if translation else 1):
        for ly in range(L if translation else 1):

            trafos.append(idx)

            idx = np.roll(idx, 1, axis=1)

        idx = np.roll(idx, 1, axis=0)

    orbit = []

    idx = np.arange(L * L)

    for t in trafos:

        o = np.zeros((L * L, L * L), dtype=np.int32)

        o[idx, t.ravel()] = 1

        orbit.append(o)

    orbit = jnp.array(orbit)

    return orbit

def get_orbit_2d_square(L, rotation=True, reflection=True, translation=True):
    ''' This function generates the group of lattice symmetries in a two-dimensional square lattice.

    Arguments:
        * ``L``: Linear dimension of the lattice.
        * ``rotation``: Boolean to indicate whether rotations are to be included
        * ``reflection``: Boolean to indicate whether reflections are to be included
        * ``translation``: Boolean to indicate whether translations are to be included

    Returns:
        A three-dimensional ``jax.numpy.array``, where the first dimension corresponds to the different
        symmetry operations and the following two dimensions correspond to the corresponding permuation matrix.
    '''

    po = get_point_orbit_2d_square(L, rotation, reflection)

    to = get_translation_orbit_2d_square(L, translation)

    orbit = jax.vmap(lambda x, y: jax.vmap(lambda a, b: jnp.dot(b, a), in_axes=(None, 0))(x, y), in_axes=(0, None))(to, po)

    orbit = orbit.reshape((-1, L**2, L**2))

    newOrbit = [tuple(x.ravel()) for x in orbit]

    uniqueOrbit = np.unique(newOrbit, axis=0).reshape(-1, L**2, L**2)

    return jnp.array(uniqueOrbit)

def get_orbit_1d(L, translation=True, reflection=True, **kwargs):
    ''' This function generates the group of lattice symmetries in a one-dimensional lattice.

    Arguments:
        * ``L``: Linear dimension of the lattice.
        * ``reflection``: Boolean to indicate whether reflections are to be included
        * ``translation``: Boolean to indicate whether translations are to be included

    Returns:
        A three-dimensional ``jax.numpy.array``, where the first dimension corresponds to the different
        symmetry operations and the following two dimensions correspond to the corresponding permuation matrix.
    '''

    def get_point_orbit_1D(L, reflection):
        return jnp.array([jnp.eye(L), jnp.fliplr(jnp.eye(L))]) if reflection else jnp.array([jnp.eye(L)])

    def get_translation_orbit_1D(L, translation):
        to = np.array([np.eye(L)] * L)
        for idx, t in enumerate(to):
            to[idx] = np.roll(t, idx, axis=1)
        return jnp.array(to) if translation else jnp.array([jnp.eye(L)])

    po = get_point_orbit_1D(L, reflection)
    to = get_translation_orbit_1D(L, translation)
    orbit = jax.vmap(lambda x, y: jax.vmap(lambda a, b: jnp.dot(a, b), in_axes=(None, 0))(x, y), in_axes=(0, None))(to, po)

    orbit = orbit.reshape((-1, L, L))
    return orbit.astype(np.int32)


import matplotlib
matplotlib.use("Agg")

import matplotlib.pyplot as plt
import numpy as np

data = np.loadtxt("sample.dat")

mean = data.mean()
sigma = data.std()

x = np.linspace(data.min(), data.max(), 100)
y = np.exp(-0.5 * ((x -mean)/sigma)**2)
y = y/(np.sqrt(2.0* np.pi * sigma**2))

plt.hist(data, alpha=0.5, bins=20, normed=True, label="Data. N={}".format(len(data)))
plt.plot(x,y, label="Estimate. mean={:.1f} sigma={:.1f}".format(mean, sigma))
plt.xlabel("x")
plt.ylabel("PDF (x)")
plt.legend(loc=2)
plt.savefig("sample.pdf")


from matplotlib import pyplot as plt
import pandas as pd
import numpy as np
import math
import scipy.stats as stats

def con_level(alpha, n, s, sigma, show=True, ignore=True):
    """
Input: alpha, n (sample size), s (sample standard deviation), sigma (population standard deviation, can ignore if ignore=True - default), show=True
Return the confidence level at alpha. Return a dictionary: {"lcl": lcl, "ucl": ucl}

+ `show`: default is `True`. Set to `False` to disable rendering.
    """
    df_v = n - 1
    con_coef = 1 - alpha

    chi2_cv_u = stats.chi2.ppf(1 - alpha / 2, df=df_v)
    chi2_cv_l = stats.chi2.ppf(alpha / 2, df=df_v)

    lcl = (df_v) * (s ** 2) / chi2_cv_u
    ucl = (df_v) * (s ** 2) / chi2_cv_l

    result = f"""{con_coef * 100:.1f}% Confidence Interval: [{lcl:.4f}, {ucl:.4f}]

S^2 (Sample Variance): {s ** 2:.4f}
Sample Size: {n}
"""
    if ignore == False:
        flag = sigma ** 2 >= lcl and sigma ** 2 <= ucl
        if flag:
            cover = "covers the real/population variance."
        else:
            cover = "does not cover the real/population variance."

        result += f'''sigma^2 (Population Variance) = {sigma ** 2:.4f}
The interval {cover}
'''
    if show:
        print(result)
    return {"lcl": lcl, "ucl": ucl}

def rejection_region_method(alpha, n, s, sigma, option='left', precision=4, show=True, ignore=False):
    """
    Input: alpha, n, s, sigma, option='left', precision=4, show=True, ignore=False
    Output:
        if opt == 't':
            return s2_l, s2_u
        else:
            return s2_c
    """
    opt = option.lower()[0]
    df_v = n - 1
    var = s ** 2
    if opt == 't':
        option = 'Two-Tail Test'
        chi2_cv_u = stats.chi2.ppf(1 - alpha / 2, df=df_v)
        chi2_cv_l = stats.chi2.ppf(alpha / 2, df=df_v)
        s2_u = chi2_cv_u * (sigma ** 2) / df_v
        s2_l = chi2_cv_l * (sigma ** 2) / df_v
        flag = var < s2_l or var > s2_u
        if not ignore:
            result = f'''======= The Rejection Region Method =======
Significant Level (alpha) = {alpha:.{precision}f}
chi2_l (Lower bound for Chi2 critical value) = {chi2_cv_l:.{precision}f}
chi2_u (Upper bound for Chi2 critical value) = {chi2_cv_u:.{precision}f}

Using {option}:
S^2 (var) =  {var:.{precision}f}
S_L^2 (Lower bound for the critical value) = {s2_l:.{precision}f}
S_U^2 (Upper bound for the critical value) = {s2_u:.{precision}f}
Reject H_0 → {flag}
            '''
        else:
            result = f'''======= The Rejection Region Method =======
Significant Level (alpha) = {alpha:.{precision}f}
chi2_l (Lower bound for Chi2 critical value) = {chi2_cv_l:.{precision}f}
chi2_u (Upper bound for Chi2 critical value) = {chi2_cv_u:.{precision}f}

Using {option}:
S_L^2 (Lower bound for the critical value) = {s2_l:.{precision}f}
S_U^2 (Upper bound for the critical value) = {s2_u:.{precision}f}
            '''

    else:
        if opt == 'l':
            # left tail
            option = 'One-Tail Test (left tail)'
            chi2_value = df_v * (s ** 2) / (sigma ** 2)
            chi2_cv = stats.chi2.ppf(alpha, df_v)
            s2_c = chi2_cv * (sigma ** 2) / df_v
            flag = var < s2_c
        elif opt == 'r':
            option = 'One-Tail Test (right tail)'
            chi2_value = df_v * (s ** 2) / (sigma ** 2)
            chi2_cv = stats.chi2.ppf(1 - alpha, df_v)
            s2_c = chi2_cv * (sigma ** 2) / df_v
            flag = var > s2_c
        if not ignore:
            result = f'''======= The Rejection Region Method =======
Significant Level (alpha) = {alpha:.{precision}f}
chi2 (Critical value) = {chi2_cv:.{precision}f}

Using {option}:
S^2 (var) =  {var:.{precision}f}
S_C^2 (Critical value) = {s2_c:.{precision}f}
Reject H_0 → {flag}
            '''
        else:
            result = f'''======= The Rejection Region Method =======
Significant Level (alpha) = {alpha:.{precision}f}
chi2 (Critical value) = {chi2_cv:.{precision}f}

Using {option}:
S_C^2 (Critical value) = {s2_c:.{precision}f}
            '''

    if show:
        print(result)

    if opt == 't':
        return s2_l, s2_u
    else:
        return s2_c

def testing_statistic_method(alpha, n, s, sigma, option='left', precision=4, ignore=False):
    """
    Input: alpha, n, s, sigma, option='left', precision=4, show=True, ignore=False
    Output:
        if opt == 't':
            return chi2_value, chi2_cv_l, chi2_cv_u
        else:
            return chi2_value, chi2_cv
    """
    opt = option.lower()[0]
    df_v = n - 1
    var = s ** 2
    chi2_value = df_v * (s ** 2) / (sigma ** 2)

    if opt == 't':
        option = 'Two-Tail Test'
        chi2_cv_u = stats.chi2.ppf(1 - alpha / 2, df=df_v)
        chi2_cv_l = stats.chi2.ppf(alpha / 2, df=df_v)
        flag = chi2_value < chi2_cv_l or chi2_value > chi2_cv_u

        if not ignore:
            result = f'''======= Testing Statistic Method =======
Significant Level (alpha) = {alpha:.{precision}f}

Using {option}:
Chi2 (Observed value) =  {chi2_value:.{precision}f}
Chi2_L (Lower bound for Chi2 critical value) = {chi2_cv_l:.{precision}f}
Chi2_U (Upper bound for Chi2 critical value) = {chi2_cv_u:.{precision}f}
Reject H_0 → {flag}
            '''
        else:
            result = f'''======= Testing Statistic Method =======
Significant Level (alpha) = {alpha:.{precision}f}

Using {option}:
Chi2_L (Lower bound for Chi2 critical value) = {chi2_cv_l:.{precision}f}
Chi2_U (Upper bound for Chi2 critical value) = {chi2_cv_u:.{precision}f}
            '''

    else:
        if opt == 'l':
            # left tail
            option = 'One-Tail Test (left tail)'
            chi2_cv = stats.chi2.ppf(alpha, df_v)
            flag = chi2_value < chi2_cv
        elif opt == 'r':
            option = 'One-Tail Test (right tail)'
            chi2_cv = stats.chi2.ppf(1 - alpha, df_v)
            flag = chi2_value > chi2_cv

        if not ignore:
            result = f'''======= Testing Statistic Method =======
Significant Level (alpha) = {alpha:.{precision}f}

Using {option}:
Chi2 (Observed value) =  {chi2_value:.{precision}f}
Chi2_C (Critical value) = {chi2_cv:.{precision}f}
Reject H_0 → {flag}
            '''

        else:
            result = f'''======= Testing Statistic Method =======
Significant Level (alpha) = {alpha:.{precision}f}

Using {option}:
Chi2_C (Critical value) = {chi2_cv:.{precision}f}
            '''

    print(result)
    if opt == 't':
        return chi2_value, chi2_cv_l, chi2_cv_u
    else:
        return chi2_value, chi2_cv

def inter_p_value(p_value):
    # interpretation
    if p_value >= 0 and p_value < 0.01:
        inter_p = 'Overwhelming Evidence'
    elif p_value >= 0.01 and p_value < 0.05:
        inter_p = 'Strong Evidence'
    elif p_value >= 0.05 and p_value < 0.1:
        inter_p = 'Weak Evidence'
    elif p_value >= .1:
        inter_p = 'No Evidence'
    return inter_p

def p_value_method(alpha, n, s, sigma, option='left', precision=4):
    """
    Input: alpha, n, s, sigma, option='left', precision=4
    Output: chi2_stat, p_value
    """
    opt = option.lower()[0]
    df_v = n - 1
    var = s ** 2
    chi2_stat = df_v * (s ** 2) / (sigma ** 2)

    if opt == 't':
        # two-tail test
        option = 'Two-Tail Test'
        alphas = np.arange(0, 1, 0.001)
        chi2s = stats.chi2.ppf(alphas, df_v)
        med = np.median(chi2s)

        if chi2_stat > med:
            p_value = 2 * stats.chi2.sf(chi2_stat, df_v)
        else:
            p_value = 2 * stats.chi2.cdf(chi2_stat, df_v)

        flag = p_value < alpha
        sub_result = f'''Using {option}:
Chi2 Median under d.f. {df_v}: {med:.{precision}f}
Chi2 Stat (Observed value) = {chi2_stat:.{precision}f}
p-value = {p_value:.{precision}f} ({inter_p_value(p_value)})
Reject H_0 → {flag}
        '''
    else:
        if opt == 'l':
            option = 'One-Tail Test (left tail)'
            p_value = stats.chi2.cdf(chi2_stat, df_v)
            chi2_cv = stats.chi2.ppf(alpha, df_v)
        elif opt == 'r':
            option = 'One-Tail Test (right tail)'
            p_value = stats.chi2.sf(chi2_stat, df_v)
            chi2_cv = stats.chi2.ppf(1 - alpha, df_v)
        flag = p_value < alpha
        sub_result = f'''Using {option}:
Chi2_C (Critical value) = {chi2_cv:.{precision}f}
Chi2 Stat (Observed value) = {chi2_stat:.{precision}f}
p-value = {p_value:.{precision}f} ({inter_p_value(p_value)})
Reject H_0 → {flag}
        '''

    result = f"""======= p-value Method =======
S^2 (var) = {var:.{precision}f}
Number of Observation = {n:.{precision}f}
Hypothesized Std. (H0 Std.) = {sigma:.{precision}f}
Sample Standard Deviation = {s:.{precision}f}
Significant Level (alpha) = {alpha:.{precision}f}

""" + sub_result

    print(result)

    return chi2_stat, p_value

# Chi-Squared Tests for two or more variables

def multinomial(freq_o, freq_e, alpha=0.05, precision=4):
    """
    >>> return chi2_stat, chi2_cv, p_value
    Check RULE of FIVE before running the test
    >>> if np.sum(freq_e < 5) > 0:
    >>>    print("Rule of five is not met. ")
    """
    # if np.sum(freq_e < 5) > 0:
    #     print("Rule of five is not met.")
    #     return
    stat, p_value = stats.chisquare(freq_o, freq_e)
    df = freq_o.shape[0]-1
    chi2_cv = stats.chi2.ppf(1 - alpha, df)
    flag = False
    if p_value < 0.05:
        flag = True
    result = f"""======= Goodness of Fit Test: A Multinomial Population =======
Chi-squared test:
chi2 statistics = {stat:.{precision}f}
chi2 critical value = {chi2_cv:.{precision}f}
Degree of freedom = {df}
p-value = {p_value:.{precision}f} ({inter_p_value(p_value)})

Reject H_0 (does not follow the probability) → {flag}
"""
    print(result)
    return stat, chi2_cv, p_value

def contingency(cont_table=None, alpha=0.05, precision=4):
    """
    Check RULE of FIVE before running the test
    >>> return chi2, p, dof, ex
    """
    chi2, p, dof, ex = stats.chi2_contingency(cont_table, correction=False)
    chi2_cv = stats.chi2.ppf(1 - alpha, dof)
    flag = False
    if p < alpha:
        flag = True

    result = f"""======= Tests of Independence: Contingency Table =======
chi2 Statistics = {chi2:.{precision}f}
chi2 critical value = {chi2_cv:.{precision}f}

Degree of freedom = {dof}
p-value = {p:.{precision}f} ({inter_p_value(p)})

Reject H_0 (dependent) → {flag}

Expected Frequency:
{ex}
"""
    print(result)
    return chi2, p, dof, ex

# Poisson

def frequency_table(interval, observed, nobs, total_time, mu):
    o_vs_e_freq_df = pd.DataFrame(columns=['i', 'f_i', 'e_i', 'f_i - e_i'])
    poisson_table = stats.poisson(mu)
    N = total_time
    for (i, f) in zip(interval, observed):
        e = poisson_table.pmf(i)
        e *= N
        o_vs_e_freq_df = o_vs_e_freq_df.append(
            {'i': i, 'f_i': f, 'e_i': e, 'f_i - e_i': f - e}, ignore_index=True)
    return o_vs_e_freq_df

def df_combine(df=None, to_combine=None):
    """
    >>> to_combine = [[0,1,2],[10, 11, 12]]
    >>> updated_df = df_combine(df=o_vs_e_freq_df, to_combine=to_combine)
    """
    if len(to_combine) == 0:
        return df
    else:
        updated_df = pd.DataFrame(columns=['i', 'f_i', 'e_i', 'f_i - e_i'])
        n = len(to_combine)
        for i in range(n):
            if i == 0:
                current_end = to_combine[i][-1]
                if i + 1 < n:
                    next_start = to_combine[i + 1][0]
                else:
                    next_start = None
                updated_df = updated_df.append(
                    df.iloc[to_combine[i]].sum(axis=0), ignore_index=True)
                updated_df.i[i] = f'{to_combine[i]}'
                updated_df = updated_df.append(
                    df.iloc[current_end + 1:next_start], ignore_index=True)
            else:
                current_start = to_combine[i][0]
                prev_end = to_combine[i - 1][-1]
                current_end = to_combine[i][-1]
                if i + 1 < n:
                    next_start = to_combine[i + 1][0]
                else:
                    next_start = None
                updated_df = updated_df.append(
                    df.iloc[to_combine[i]].sum(axis=0), ignore_index=True)
                updated_df.iloc[current_start - prev_end +
                                (i - 1), 0] = f'{to_combine[i]}'
                updated_df = updated_df.append(
                    df.iloc[current_end + 1:next_start], ignore_index=True)

        return updated_df

def poisson_test(df, alpha=0.05, precision=4):
    """
    >>> return chi2_stat, chi2_cv, dof
    """
    dof = df.shape[0] - 1 - 1
    chi2_stat = sum((df['f_i - e_i']) ** 2 / df['e_i'])
    chi2_cv = stats.chi2.ppf(1 - alpha, df=dof)
    flag = False  # cannot reject
    if chi2_stat > chi2_cv:
        flag = True

    result = f"""======= Goodness of Fit Test: Poisson Test =======
chi2 statistic (observed value): {chi2_stat:.{precision}f}
chi2 critical value: {chi2_cv:.{precision}f}
Degree of freedom: {dof}

Reject H_0 (does not follow a Poisson distribution) → {flag}
"""
    print(result)
    return chi2_stat, chi2_cv, dof

def normal_test(srs=None, z_intervals=None, freq_o=None, interval=None, alpha=0.05, precision=4):
    """
    pass in Series instead of DataFrame
    >>> return o_vs_e_freq_df, chi2_stat, chi2_cv, dof

    Note:
    >>> z_value = stats.norm.ppf(area)
    """
    if z_intervals is not None:
        o_vs_e_freq_df = pd.DataFrame(columns=['i', 'f_i', 'e_i', 'f_i - e_i'])

        # intervals = [(-1), (-1, 0), (0, 1), (1)]
        # freq_o = np.array([6, 27, 14, 3])
        nobs = np.sum(freq_o)

        snd = stats.norm

        for i, interval in enumerate(z_intervals):
            try:
                if len(interval) > 1:
                    z_value = snd.cdf(interval[1]) - snd.cdf(interval[0])
            except:
                if i == 0:
                    z_value = snd.cdf(interval)
                else:
                    z_value = snd.sf(interval)

            e_i = nobs * z_value
            o_vs_e_freq_df = o_vs_e_freq_df.append(
                {'i': interval, 'f_i': freq_o[i], 'e_i': e_i, 'f_i - e_i': freq_o[i] - e_i}, ignore_index=True)

        if np.sum(o_vs_e_freq_df['e_i'] < 5) > 0:
            print("Rule of five is not met. ")

        chi2_stat = np.sum(o_vs_e_freq_df['f_i - e_i']
                           ** 2 / o_vs_e_freq_df['e_i'])
        dof = o_vs_e_freq_df.shape[0] - 2 - 1
        chi2_cv = stats.chi2.ppf(1 - alpha, df=dof)
        flag = False
        if chi2_stat > chi2_cv:
            flag = True
        result = f"""======= Goodness of Fit Test: Normal Distribution =======
chi2 statistics = {chi2_stat:.{precision}f}
chi2 critical value = {chi2_cv:.{precision}f}
Degree of freedom = {dof}

Reject H_0 (reject the assumption that the population is normally distributed) → {flag}
    """
        print(result)

        return o_vs_e_freq_df, chi2_stat, chi2_cv, dof

    elif srs is not None:
        snd = stats.norm
        arr = np.array(srs)
        o_vs_e_freq_df = pd.DataFrame(columns=['i', 'f_i', 'e_i', 'f_i - e_i'])
        n = len(arr)
        mu = arr.mean()
        std = arr.std(ddof=1)
        if interval == None:
            interval = n / 5
            exp = 5
            area = 1 / interval
            acc_area = area
            ctr = 0
        else:
            area = 1 / interval
            acc_area = area
            ctr = 0
            exp = area * n
        if exp < 5:
            print("Rule of five is not met.")

        while abs(acc_area - 1) > 0.00000001:
            #             print(acc_area)
            z_value = snd.ppf(acc_area)
            x_value = z_value * std + mu
            acc_area += area
            f_i = sum(arr <= x_value)
            arr = arr[arr > x_value]

            ctr += 1
            o_vs_e_freq_df = o_vs_e_freq_df.append(
                {'i': x_value, 'f_i': f_i, 'e_i': exp, 'f_i - e_i': f_i - exp}, ignore_index=True)

        num_df = o_vs_e_freq_df.i
        f_i = sum(arr > x_value)
        x_value = f'> {x_value:.3f}'
        o_vs_e_freq_df = o_vs_e_freq_df.append(
            {'i': x_value, 'f_i': f_i, 'e_i': exp, 'f_i - e_i': f_i - exp}, ignore_index=True)

        for i in range(1, ctr):
            o_vs_e_freq_df.iat[i, 0] = f'{num_df[i - 1]:.3f} ~ {num_df[i]:.3f}'
        o_vs_e_freq_df.iat[0, 0] = f'<= {o_vs_e_freq_df.i[0]:.3f}'

        # test
        chi2_stat = np.sum(o_vs_e_freq_df['f_i - e_i']
                           ** 2 / o_vs_e_freq_df['e_i'])
        dof = o_vs_e_freq_df.shape[0] - 2 - 1
        chi2_cv = stats.chi2.ppf(1 - alpha, df=dof)
        flag = False
        if chi2_stat > chi2_cv:
            flag = True
        result = f"""======= Goodness of Fit Test: Normal Distribution =======
chi2 statistics = {chi2_stat:.{precision}f}
chi2 critical value = {chi2_cv:.{precision}f}
Degree of freedom = {dof}

μ = {mu:.{precision}f}
σ = {std:.{precision}f}

Reject H_0 (reject the assumption that the population is normally distributed with μ = {mu:.{precision}f} and σ = {std:.{precision}f}) → {flag}
        """
        print(result)

        return o_vs_e_freq_df, chi2_stat, chi2_cv, dof


import numpy as np

# Usando list comprehension para indexar

A = np.array([[n+m*10 for n in range(5)] for m in range(5)])
A

"""
    La salida será

    array([[ 0,  1,  2,  3,  4],
       [10, 11, 12, 13, 14],
       [20, 21, 22, 23, 24],
       [30, 31, 32, 33, 34],
       [40, 41, 42, 43, 44]])
"""

# Aplicando máscaras
B = np.array([n for n in range(5)])
print(B)    # [0 1 2 3 4]

row_mask = np.array([True, False, True, False, False])
print(B[row_mask])  # [0 2]

# Con máscaras podemos seleccionar condicionalmente elementos de un array
x = np.arange(0, 10, 0.5)
print(x)

"""
    La salida será

[ 0.   0.5  1.   1.5  2.   2.5  3.   3.5  4.   4.5  5.   5.5  6.   6.5  7.
  7.5  8.   8.5  9.   9.5]
"""

mask = (5 < x) * (x < 7.5)
print(mask)

"""
    La salida será

    [False False False False False False False False False False False  True
  True  True  True False False False False False]
"""


""" trapSerial.py
    Example usage:
        $ python trapSerial.py 0.0 1.0 10000
        With 10000 trapezoids, the estimate of the integral of x^2 from 0.0 to 1.0 is:
            0.333333335
"""

from __future__ import division
from sys import argv
import numpy as np

def integrate_range(fxn, a, b, n):
    ''' Numerically integrates the function fxn by the trapezoid rule
        Integrates from a to b with n trapezoids
        '''
    # There are n trapezoids and therefore there are n+1 endpoints
    endpoints = np.linspace(a, b, n+1)

    integral = sum(fxn(x) for x in endpoints)
    integral -= (fxn(a) + fxn(b))/2
    integral *= (b - a)/n

    return integral

# An arbitrary test function to integrate
def function(x):
    return x**2

# Read the command line arguments
a = float(argv[1])
b = float(argv[2])
n = int(argv[3])

result = integrate_range(function, a, b, n)
print "With {0} trapezoids, the estimate of the integral of x^2 from {1} to {2} is: \n\t{3}".format(n, a, b, result)


#Given a 1D array, negate all elements which are between 3 and 8, in place

import numpy as np

arr = np.arange(8)
m = (arr > 2) & (arr < 8)
arr[m] *= -1
print(arr)

'''
Output

[ 0  1  2 -3 -4 -5 -6 -7]

'''


"""
Useful functions to deal with 3D geometry
"""

# pylint: disable=invalid-name,missing-docstring,invalid-unary-operand-type,no-else-return

import numpy as np

def get_plane(a, b, c):
    """
    Get plane equation from 3 points.
    Returns the coefficients of `ax + by + cz + d = 0`
    """
    ab = b - a
    ac = c - a

    x = np.cross(ab, ac)
    d = -np.dot(x, a)
    pl = np.hstack((x, d))
    return pl

def point_plane_dist(pt, plane, signed=False):
    """
    Get the signed distance from a point `pt` to a plane `plane`.
    Reference: http://mathworld.wolfram.com/Point-PlaneDistance.html

    Plane is of the format [A, B, C, D], where the plane equation is Ax+By+Cz+D=0
    Point is of the form [x, y, z]
    `signed` flag indicates whether to return signed distance.
    """
    v = plane[0:3]
    dist = (np.dot(v, pt) + plane[3]) / np.linalg.norm(v)

    if signed:
        return dist
    else:
        return np.abs(dist)

def edges_of(vertices):
    """
    Return the vectors for the edges of the polygon defined by `vertices`.

    Args:
        vertices: list of vertices of the polygon.
    """
    edges = []
    N = len(vertices)

    for i in range(N):
        edge = vertices[(i + 1) % N] - vertices[i]
        edges.append(edge)

    return edges

def orthogonal(v):
    """
    Return a 90 degree clockwise rotation of the vector `v`.

    Args:
        v: 2D array representing a vector.
    """
    return np.array([-v[1], v[0]])

def is_separating_axis(o, p1, p2):
    """
    Return True and the push vector if `o` is a separating axis of `p1` and `p2`.
    Otherwise, return False and None.

    Args:
        o: 2D array representing a vector.
        p1: 2D array of points representing a polygon.
        p2: 2D array of points representing a polygon.
    """
    min1, max1 = float('+inf'), float('-inf')
    min2, max2 = float('+inf'), float('-inf')

    for v in p1:
        projection = np.dot(v, o)

        min1 = min(min1, projection)
        max1 = max(max1, projection)

    for v in p2:
        projection = np.dot(v, o)

        min2 = min(min2, projection)
        max2 = max(max2, projection)

    if max1 >= min2 and max2 >= min1:
        d = min(max2 - min1, max1 - min2)
        # push a bit more than needed so the shapes do not overlap in future
        # tests due to float precision
        d_over_o_squared = d/np.dot(o, o) + 1e-10
        pv = d_over_o_squared*o
        return False, pv
    else:
        return True, None

def polygon_collision(p1, p2):
    """
    Return True if the shapes collide. Otherwise, return False.

    p1 and p2 are np.arrays, the vertices of the polygons in the
    counterclockwise direction.

    Source: https://hackmd.io/s/ryFmIZrsl

    Args:
        p1: 2D array of points representing a polygon.
        p2: 2D array of points representing a polygon.
    """
    edges = edges_of(p1)
    edges += edges_of(p2)
    orthogonals = [orthogonal(e) for e in edges]

    push_vectors = []
    for o in orthogonals:
        separates, pv = is_separating_axis(o, p1, p2)

        if separates:
            # they do not collide and there is no push vector
            return False
        else:
            push_vectors.append(pv)

    return True

def polygon_area(polygon):
    """
    Get the area of a polygon which is represented by a 2D array of points.
    Area is computed using the Shoelace Algorithm.

    Args:
        polygon: 2D array of points.
    """
    x = polygon[:, 0]
    y = polygon[:, 1]
    area = (np.dot(x, np.roll(y, -1)) -
            np.dot(np.roll(x, -1), y))
    return np.abs(area)/2

def polygon_intersection(poly1, poly2):
    """
    Use the Sutherland-Hodgman algorithm to compute the intersection of 2 convex polygons.
    """
    def line_intersection(e1, e2, s, e):
        dc = e1 - e2
        dp = s - e
        n1 = np.cross(e1, e2)
        n2 = np.cross(s, e)
        n3 = 1.0 / (np.cross(dc, dp))
        return np.array([(n1*dp[0] - n2*dc[0]) * n3, (n1*dp[1] - n2*dc[1]) * n3])

    def is_inside_edge(p, e1, e2):
        """Return True if e is inside edge (e1, e2)"""
        return np.cross(e2-e1, p-e1) >= 0

    output_list = poly1
    # e1 and e2 are the edge vertices for each edge in the clipping polygon
    e1 = poly2[-1]

    for e2 in poly2:
        input_list = output_list
        output_list = []
        s = input_list[-1]

        for e in input_list:
            if is_inside_edge(e, e1, e2):
                # if s in not inside edge (e1, e2)
                if not is_inside_edge(s, e1, e2):
                    # line intersects edge hence we compute intersection point
                    output_list.append(line_intersection(e1, e2, s, e))
                output_list.append(e)
            # is s inside edge (e1, e2)
            elif is_inside_edge(s, e1, e2):
                output_list.append(line_intersection(e1, e2, s, e))

            s = e
        e1 = e2

    return np.array(output_list)


import os
import time
import numpy as np
import scipy as sp
import matplotlib.pyplot as plt
from mpl_toolkits import mplot3d
from scipy import signal
import math

# School ID = 03118942
# F=9+4+2=15=>1+5=6

myFreq=8000
A=4

#-----------Βοηθητικες Συναρτησεις------------

def PlotYLim(Max, Min):
	plt.ylim([Min,Max])

def plotSignals(time1, signal1, color1, legend1, PlotTitle, numberOfSignals, freq, numOfPeriods, time2=None, signal2=None, color2=None, legend2=None):
	if numberOfSignals==1:
		plt.plot(time1, signal1, color1)
		plt.legend(legend1)
	elif numberOfSignals==2:
		plt.plot(time1, signal1, color1, time2, signal2, color2)
		plt.legend([legend1, legend2])
	else:
		return None
	plt.xticks(np.arange(0, numOfPeriods/freq, 1/(2*freq)))
	plt.xlabel('Time [s]')
	plt.ylabel('Amplitude [V]')
	plt.title(PlotTitle)
	plt.grid()
	plt.show()

#---------------|ΑΣΚΗΣΗ 1|-------------------

#(A)
fs1=30*myFreq #180kHz
fs2=50*myFreq #300kHz

t1 = np.arange(0, 4/myFreq, 1/fs1)
t2 = np.arange(0, 4/myFreq, 1/fs2)
triangle1 = 4*signal.sawtooth(2 * np.pi * myFreq * t1, 0.5)
triangle2 = 4*signal.sawtooth(2 * np.pi * myFreq * t2, 0.5)

plotSignals(t1, triangle1, 'o', 'Fs1', 'Fs1 Sampling Rate', 1, myFreq, 4.5)

plotSignals(t2, triangle2, 'o', 'Fs2', 'Fs2 Sampling Rate', 1, myFreq, 4.5)

#ploting Both
plotSignals(t1, triangle1, 'o', 'Fs1', 'Both Sampling Rates', 2, myFreq, 4.5, t2, triangle2, 'o', 'Fs2')

#(B)
fs3 = 4*myFreq #24kHz
t3 = np.arange(0, 4.25/myFreq, 1/fs3)
triangle3 = 4*signal.sawtooth(2 * np.pi * myFreq * t3, 0.5)
plotSignals(t3, triangle3, 'o', 'Fs3', 'Fs3 Sampling Rate', 1, myFreq, 4.5)

#(C)

#(i)
def getSin(time):
	return np.sin(np.pi*2*myFreq*time)

#(a)
plotSignals(t1, getSin(t1), 'o', 'Fs1', 'Sin Sampling Rate Fs1', 1, myFreq, 4.5)

plotSignals(t2, getSin(t2), 'o', 'Fs2', 'Sin Sampling Rate Fs2', 1, myFreq, 4.5)

plotSignals(t1, getSin(t1), 'o', 'Fs1', 'Sin Both Sampling Rates', 2, myFreq, 4.5, t2, getSin(t2), 'o', 'Fs2')

#(b)
plotSignals(t3, getSin(t3), 'o', 'Fs3', 'Sin Sampling Rate Fs3', 1, myFreq, 4.5)

#(ii)
addedFreq=1000 #1 kHz

def getQ(time):
	return getSin(time) + np.sin(np.pi*2*(myFreq+addedFreq)*time)

#(a)
fc = math.gcd(myFreq, myFreq + addedFreq) # κοινή συχνότητα
t1 = np.arange(0, 1/fc, 1/fs1)
t2 = np.arange(0, 1/fc, 1/fs2)
t3 = np.arange(0, 1/fc, 1/fs3)

plotSignals(t1, getQ(t1), 'o', 'Fs1', 'Q Signal Sampling Rate Fs1', 1, fc, 1.5)

plotSignals(t2, getQ(t2), 'o', 'Fs1', 'Q Signal Sampling Rate Fs1', 1, fc, 1.5)

plotSignals(t1, getQ(t1), 'o', 'Fs1', 'Q Signal Both Sampling Rates', 2, fc, 1.5, t2, getQ(t2), 'o', 'Fs2')

#(b)
plotSignals(t3, getQ(t3), 'o', 'Fs3', 'Q Signal Sampling Rate Fs3', 1, fc, 1.5)

#---------------|ΑΣΚΗΣΗ 2|-------------------

# Θα χρεισταστουμε την συνχοτητα fs1 και το t1 του πρωτου ερωτηματος

fig, ax = plt.subplots()

ax.yaxis.set_major_formatter(StrMethodFormatter("{x:04b}"))


# =============================================================================
#
# Explicit Finite Difference Method Code to Solve the 1D Diffusion/Heat Equation
# Adapted by: Cameron Armstrong (2019)
# Source: Lorena Barba, 12 Steps to NS in Python
# Institution: Virginia Commonwealth University
#
# =============================================================================

# Required Modules
import numpy as np
from matplotlib import pyplot as plt

# Grid and Numerical Parameters
xl = 2                                      # x length
nx = 100                                    # number of grid points
x = np.linspace(0,2,nx)                     # x grid
dx = xl/(nx-1)                              # x stepsize
nt = 500                                    # number of time steps
nu = 0.01                                   # diffusivity term
sigma = 0.2                                 # parameter for numerical stability
dt = sigma*dx**2/nu                         # time step defined using nu and sigma

# Array Initialization and Plot Conditions
u = np.zeros(nx)                            # initializing solution array
un = np.zeros(nx)                           # initializing temporary solution array
u[int(.8/dx):int(1.2/dx+1)]=1               # initial condition
plt.plot(x,u)                               # plotting initial condition (IC)
plotcond = np.zeros(nt)                     # plot condition, determines when to plot solution
p = 1                                       # plot condition counter
f = 50                                      # plot frequency

for n in range(nt): # main time-marching loop
    un = u.copy() # update temporary solution array as IC
#    u[0]=u[1] #neumann BC
#    u[-1]=u[-2] #neumann BC
    u[0]=0 #dirichlet BC
    u[nx-1]=0 #dirichlet BC
    u[1:-1] = un[1:-1] + nu*dt/dx**2*(un[2:]-2*un[1:-1]+un[:-2]) # vectorized FDM solution using forward Euler and CDS
    plotcond[n] = np.round((n/f/p),1) # logging plot condition
    plotcond[n] = plotcond[n].astype(int) # converting to an integer
    if plotcond[n] == 1: #checking plot condition
        plt.figure(1)
        plt.plot(x,u,label='t = %s s' %(np.round(n*dt,1)))
        plt.legend()
        #plt.ylim(0,1.2)
        p += 1 # updating plot condition counter


'''
Data pre-processing for keras and sklearn

(good tutorial here)
'''
import numpy as np
from numpy import argmax, array
from sklearn.preprocessing import LabelEncoder
from sklearn.preprocessing import OneHotEncoder
from keras.utils import to_categorical

# define example
data = ['cold', 'cold', 'warm', 'cold', 'hot', 'hot', 'warm', 'cold', 'warm', 'hot']

# one hot encoding via sklearn library
values = np.array(data)
print(values)
# integer encode
label_encoder = LabelEncoder()
integer_encoded = label_encoder.fit_transform(values)
print(integer_encoded)
# binary encode
onehot_encoder = OneHotEncoder(sparse=False)
integer_encoded = integer_encoded.reshape(len(integer_encoded), 1)
onehot_encoded = onehot_encoder.fit_transform(integer_encoded)
print(onehot_encoded)
# invert first example
inverted = label_encoder.inverse_transform([argmax(onehot_encoded[0, :])])
print(inverted)

# one hot encoding via keras library
data = [1, 3, 2, 0, 3, 2, 2, 1, 0, 1]
data = array(data)
print(data)
# one hot encode
encoded = to_categorical(data)
print(encoded)
# invert encoding
inverted = argmax(encoded[0])
print(inverted)
# to get back original list
inverted=list()
for i in range(len(encoded)):
    invert=argmax(encoded[i])
    inverted.append(invert)

inverted=array(inverted)
print(inverted)


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

def thres(x, mu):
    """
    y = sgn(x)max(|x| - mu, 0)

    Parameters
    ----------
    x: numpy array
    mu: thresholding parameter

    Returns:
    ----------
    y: numpy array

    """
    y = np.maximum(x - mu, 0)
    y = y + np.minimum(x + mu, 0)
    return y

def solve_proj2(m, U, lambda1, lambda2):
    """
    solve the problem:
    min_{v, s} 0.5*|m-Uv-s|_2^2 + 0.5*lambda1*|v|^2 + lambda2*|s|_1

    solve the projection by APG

    Parameters
    ----------
    m: nx1 numpy array, vector
    U: nxp numpy array, matrix
    lambda1, lambda2: tuning parameters

    Returns:
    ----------
    v: px1 numpy array, vector
    s: nx1 numpy array, vector

    """
    # intialization
    n, p = U.shape
    v = np.zeros(p)
    s = np.zeros(n)
    I = np.identity(p)
    converged = False
    maxIter = np.inf
    k = 0
    # alternatively update
    UUt = np.linalg.inv(U.transpose().dot(U) + lambda1*I).dot(U.transpose())
    while not converged:
        k += 1
        vtemp = v
        # v = (U'*U + lambda1*I)\(U'*(m-s))
        v = UUt.dot(m - s)
        stemp = s
        s = thres(m - U.dot(v), lambda2)
        stopc = max(np.linalg.norm(v - vtemp), np.linalg.norm(s - stemp))/n
        if stopc < 1e-6 or k > maxIter:
            converged = True

    return v, s


"""Matrix multiplication.

Perform matrix multiplication of a real matrix _a with _nx rows and _ny columns, a real matrix _b with _ny rows and _nz columns and assign the value to a real matrix _c with _nx rows and _nz columns.

Source: tkoenig
"""

# Implementation author: programming-idioms.org
# Created on 2019-09-27T16:40:03.906529Z
# Last modified on 2019-09-27T16:40:03.906529Z
# Version 1

# You can also use _np._matrix instead of _np._array. Be careful when using _array, because the * operator performs elementwise multiplication.

import numpy as np

c = np.matmul(a, b)


import matplotlib
matplotlib.rcParams = matplotlib.rc_params_from_file('../../matplotlibrc')
import numpy as np
from scipy.misc import imsave
from scipy.linalg import svd
from os import walk
from scipy.ndimage import imread

def getFaces(path="./faces94"):
    # these are the dimensions of the images
    w = 200
    h = 180

    # traverse the directory, get one image per subdirectory
    faces = []
    for (dirpath, dirnames, filenames) in walk(path):
        for f in filenames:
            if f[-3:]=="jpg": # only get jpg images
                # load image, convert to grayscale, flatten into vector
                face = imread(dirpath+"/"+f).mean(axis=2).ravel()
                faces.append(face)
                break

    # put all the face vectors column-wise into a matrix
    F = np.array(faces).T
    return F

def plotMeanFace(mu):
    imsave("meanFace.png", mu.reshape((200, 180)))

def plotDiffFaces(A):
    for j, i in enumerate([0,20,50]):
        imsave("differenceFace{0}.png".format(j),A[:,i].reshape((200,180)))

def plotEigenFaces(U):
    for i in xrange(3):
        imsave("eigenface{0}.png".format(i),U[:,i].reshape((200,180)))

def plotReconstructed(U, A, mu):
    imsave("rebuiltAll.png", (A[:,20]+mu).reshape((200,180)))
    s = U.shape[1]
    r1 = U[:,:75].dot(U[:,:75].T.dot(A[:,0])) + mu
    r2 = U[:,:38].dot(U[:,:38].T.dot(A[:,0])) + mu
    r3 = U[:,:19].dot(U[:,:19].T.dot(A[:,0])) + mu
    r4 = U[:,:9].dot(U[:,:9].T.dot(A[:,0])) + mu
    r5 = U[:,:5].dot(U[:,:5].T.dot(A[:,0])) + mu
    imsave("rebuiltHalf.png", r1.reshape((200,180)))
    imsave("rebuiltFourth.png", r2.reshape((200,180)))
    imsave("rebuiltEighth.png", r3.reshape((200,180)))
    imsave("rebuiltSixteenth.png", r4.reshape((200,180)))
    imsave("rebuiltThirtySecond.png", r5.reshape((200,180)))

if __name__ == "__main__":
    F = getFaces(path="../../../faces94")
    print F.shape
    mu = F.mean(axis=1)
    A = F - mu.reshape((len(mu), 1))
    U,Sig,Vh = svd(A, full_matrices=False)
    plotMeanFace(mu)
    plotDiffFaces(A)
    plotEigenFaces(U)
    plotReconstructed(U, A, mu)