from __future__ import unicode_literals
from math import sqrt
import random
from random import randint as rand
from random import getrandbits
import sympy
import time
import binascii

def gcd(a, b):
    if b == 0:
        return a
    else:
        return gcd(b, a % b)

def egcd(a, b):
    x,y, u,v = 0,1, 1,0
    while a != 0:
        q, r = b//a, b%a
        m, n = x-u*q, y-v*q
        b,a, x,y, u,v = a,r, u,v, m,n
    return b, x, y

def mod_inverse(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        return None
    else:
        return x % m

def isprime(n):
    if n < 2:
        return False
    elif n == 2:
        return True
    else:
        for i in range(1, int(sqrt(n)) + 1):
            if n % i == 0:
                return False
    return True

def generate_prime(bitlength):
    a = '1'+'0'*(bitlength-1)
    b = '1'*bitlength

    p = sympy.randprime(int(a, 2), int(b, 2))
    return p

def generate_keypair(keysize):
    p = generate_prime(keysize)
    q = generate_prime(keysize)
    print(p, q)
    n = p * q
    phi = (p-1)*(q-1)//gcd(p-1, q-1)
    e = sympy.randprime(1,phi)
    d = mod_inverse(e,phi)
    if e != d:
        return ((e, n), (d, n))

def encrypt(plain_text, package):
    e, n = package
    if plain_text > n:
        print('Message is too large for key to handle')
    msg_ciphertext = pow(plain_text, e, n)
    return msg_ciphertext

def decrypt(msg_ciphertext, package):
    d, n = package
    msg_plaintext = pow(msg_ciphertext, d, n)
    return binascii.unhexlify(hex(msg_plaintext)[2:]).decode()

public, private = generate_keypair(1024)
print('Public Key : ',public)
print('Private Key : ',private)

msg = input("Write msg: ")

hex_data   = binascii.hexlify(msg.encode())
plain_text = int(hex_data, 16)

t1 = time.time()
encrypted_msg = encrypt(plain_text, public)
print("Encrypted msg: " + str(encrypted_msg))

decrypted_msg = decrypt(encrypted_msg, private)
print("Decrypted msg: " + str(decrypted_msg))
print('Runtime : ' + str(time.time()-t1) + ' seconds')


import numpy as np

def diffdrive(x, y, theta, v_l, v_r, t, l):
    # straight line
    if (v_l == v_r):
        theta_n = theta
        x_n = x + v_l * t * np.cos(theta)
        y_n = y + v_l * t * np.sin(theta)
    # circular motion
    else:
        # Calculate the radius
        R = l / 2.0 * ((v_l + v_r) / (v_r - v_l))
        # computing center of curvature
        ICC_x = x - R * np.sin(theta)
        ICC_y = y + R * np.cos(theta)
        # compute the angular velocity
        omega = (v_r - v_l) / l
        # computing angle change
        dtheta = omega * t
        # forward kinematics for differential drive
        x_n = np.cos(dtheta) * (x - ICC_x) - np.sin(dtheta) * (y - ICC_y) + ICC_x
        y_n = np.sin(dtheta) * (x - ICC_x) + np.cos(dtheta) * (y - ICC_y) + ICC_y
        theta_n = theta + dtheta

    return x_n, y_n, theta_n


from __future__ import division
from __future__ import absolute_import
import numpy as np
import pandas as pd
import scipy as sp
from rankit.Table import Table
from scipy.sparse import coo_matrix
from scipy.sparse.linalg import lsqr
from .matrix_build import fast_colley_build
from numpy.linalg import norm

class UnsupervisedRanker(object):
    """Base class for all unsupervised ranking algorithms."""
    def rank(self, table, **kargs):
        raise NotImplementedError("UnsupervisedRanker is a abstract class.")

    def _showcase(self, table, ascending=False):
        # one need to translate item index to item name.
        indexlut = table.indexlut
        rating = self.rating # iitm, rating
        itemname = []
        for row in rating.itertuples(index=False, name=None):
            itemname.append(indexlut[row[0]])
        rst = pd.DataFrame({
            "name": itemname,
            "rating": rating["rating"]})
        rst['rank'] = rst.rating.rank(method='min', ascending=ascending).astype(np.int32)
        return rst.sort_values(by=['rating', 'name'], ascending=ascending).reset_index(drop=True)

class MasseyRanker(UnsupervisedRanker):
    """Massey ranking system proposed by Kenneth Massey: Statistical models applied to the rating of sports teams.
    Bachelor's thesis, Bluefield College, 1997.
    Core idea: The competition score difference is the rating difference, so one can solve a linear equation by minimize least square error.

    Parameters
    ----------
    drawMargin: [0, +Inf), default 0.
        When absolute difference between two teams are smaller than drawMargin, this competition is considered as a tie.
    """
    def __init__(self, drawMargin = 0.0):
        self.drawMargin = drawMargin

    def rank(self, table):
        """Calculate the rank and rating with specified parameters.

        Parameters
        ----------
        table: Table
            The record table to be ranked, should be a Table object.

        Returns
        -------
        pandas.DataFrame, with column ['name', 'rating', 'rank']
        """
        drawMargin = self.drawMargin
        data = table.table[['hidx', 'vidx', 'hscore', 'vscore', 'weight']]

        m = data.shape[0]
        n = table.itemnum
        y = np.zeros(m)
        dat = np.zeros(m*2, dtype=np.float)
        col = np.zeros(m*2, dtype=np.int)
        row = np.zeros(m*2, dtype=np.int)
        for i, itm in enumerate(data.itertuples(index=False, name=None)):
            row[i*2]=i; col[i*2]=itm[0]; dat[i*2]=itm[4];
            row[i*2+1]=i; col[i*2+1]=itm[1]; dat[i*2+1]=-itm[4];
            if np.abs(itm[2]-itm[3])<=drawMargin:
                y[i]=0.0
            else:
                y[i] = itm[4]*(itm[2]-itm[3])

        X = coo_matrix((dat, (row, col)), shape=(m, n))
        X = X.tocsr()

        rst = lsqr(X, y)
        rating = rst[0]
        if hasattr(self, "rating"):
            self.rating["rating"] = rating
        else:
            self.rating = pd.DataFrame({
                "iidx": np.arange(n, dtype=np.int),
                "rating": rating})

        return self._showcase(table, False)

class ColleyRanker(UnsupervisedRanker):
    """Colley ranking system proposed by Wesley Colley:
    Colley's bias free college football ranking method: The colley matrix explained, 2002.
    http://www.colleyrankings.com
    Core idea: All team's rating starts from 0.5, and with evolvement of games, the rating of each player deviates from 0.5
    according to probability of win. However, the average rating of all teams remains 0.5.

    Parameters
    ----------
    drawMargin: [0, +Inf), default 0.
        When absolute difference between two teams are smaller than drawMargin, this competition is considered as a tie.
    """
    def __init__(self, drawMargin = 0.0):
        self.drawMargin = drawMargin

    def rank(self, table):
        """Calculate the rank and rating with specified parameters.

        Parameters
        ----------
        table: Table
            The record table to be ranked, should be a Table object.

        Returns
        -------
        pandas.DataFrame, with column ['name', 'rating', 'rank']
        """
        drawMargin = self.drawMargin
        data = table.table[['hidx', 'vidx', 'hscore', 'vscore', 'weight']]

        idx = data.iloc[:, :2]
        score = data.iloc[:, 2:]
        C, b = fast_colley_build(np.require(idx, dtype=np.int32), np.require(score, dtype=np.float64),
                                 table.itemnum, drawMargin)

        rating = sp.linalg.solve(C, b)
        if hasattr(self, "rating"):
            self.rating["rating"] = rating
        else:
            self.rating = pd.DataFrame({
                "iidx": np.arange(table.itemnum, dtype=np.int),
                "rating": rating})

        return self._showcase(table, False)

class KeenerRanker(UnsupervisedRanker):
    """Keener ranking system proposed by James Keener:
    The Perron-Frobenius theorem and the ranking of football teams, SIAM Review, 35(1):80-93, 1993
    The core idea are: 1. rating is proportional to real strength; 2. real strength is measured relatively by competitors' strength.

    Parameters
    ----------
    func: default None.
        If set, the score difference should be transformed by the function first then used for rating calculation.
    epsilon: [0, +Inf) default 1e-4
        The small value that applies an interference to game result that force each team had at least one game with each other.
    threshold: (0, +Inf), default 1e-4
        The threshold that controls when the algorithm will converge.
    """
    def __init__(self, func=None, epsilon=1e-4, threshold=1e-4):
        self.func = func
        self.epsilon = epsilon
        self.threshold = threshold

    def rank(self, table):
        """Calculate the rank and rating with specified parameters.

        Parameters
        ----------
        table: Table
            The record table to be ranked, should be a Table object.

        Returns
        -------
        pandas.DataFrame, with column ['name', 'rating', 'rank']
        """
        func, epsilon, threshold = self.func, self.epsilon, self.threshold
        mtx = pd.DataFrame(data={
            'hidx': pd.concat([table.table.hidx, table.table.vidx]),
            'vidx': pd.concat([table.table.vidx, table.table.hidx]),
            'hscore': pd.concat([table.table.hscore, table.table.vscore]),
            'vscore': pd.concat([table.table.vscore, table.table.hscore]),
            'weight': pd.concat([table.table.weight, table.table.weight])
        }, columns = ['hidx', 'vidx', 'hscore', 'vscore', 'weight']).reset_index(drop=True)
        mtx['score'] = mtx.hscore+mtx.vscore
        mtx['hscore'] = (mtx['hscore']+1)/(mtx['score']+2)
        mtx['vscore'] = (mtx['vscore']+1)/(mtx['score']+2)
        if func is not None:
            mtx['hscore'] = mtx.hscore.apply(func)
            mtx['vscore'] = mtx.vscore.apply(func)
        mtx['hscore'] = mtx['hscore']*mtx['weight']
        mtx['vscore'] = mtx['vscore']*mtx['weight']
        mtx = mtx.groupby(['hidx', 'vidx'])[['hscore', 'vscore']].mean()
        mtx.reset_index(inplace=True)

        D = coo_matrix((mtx.hscore.values, (mtx.hidx.values, mtx.vidx.values)), shape=(table.itemnum, table.itemnum)).tocsr()

        r = np.ones(table.itemnum)/table.itemnum
        pr = np.ones(table.itemnum)
        while norm(pr-r)>threshold:
            pr = r
            rho = np.sum(r)*epsilon
            r = D.dot(r)+rho*np.ones(table.itemnum)
            r /= np.sum(r)

        if hasattr(self, "rating"):
            self.rating["rating"] = r
        else:
            self.rating = pd.DataFrame({
                "iidx": np.arange(table.itemnum, dtype=np.int),
                "rating": r})
        return self._showcase(table, False)

class MarkovRanker(UnsupervisedRanker):
    """Markov ranking is actually PageRank.
    The core idea is voting: in each game, each team will vote to each other by the number of scores they lost.
    If there are multiple games for a certain pair of player, their scores will be grouped and averaged.

    Parameters
    ----------
    restart: [0, 1], default 0.3.
        Random walk with restart: in order to avoid black hole in random walk graph.
    threshold: (0, +Inf), default 1e-4
        The threshold that controls when the algorithm will converge.
    """
    def __init__(self, restart=0.3, threshold=1e-4):
        self.restart = restart
        self.threshold = threshold

    def rank(self, table):
        """Calculate the rank and rating with specified parameters.

        Parameters
        ----------
        table: Table
            The record table to be ranked, should be a Table object.

        Returns
        -------
        pandas.DataFrame, with column ['name', 'rating', 'rank']
        """
        restart, threshold = self.restart, self.threshold
        if restart>1 or restart<0:
            raise ValueError("restart rate should be between 0 and 1.")
        mtx = pd.DataFrame(data={
            'hidx': pd.concat([table.table.hidx, table.table.vidx]),
            'vidx': pd.concat([table.table.vidx, table.table.hidx]),
            'hscore': pd.concat([table.table.hscore, table.table.vscore]),
            'vscore': pd.concat([table.table.vscore, table.table.hscore]),
            'weight': pd.concat([table.table.weight, table.table.weight])
        }, columns = ['hidx', 'vidx', 'hscore', 'vscore', 'weight']).reset_index(drop=True)
        mtx['hscore'] = mtx['hscore']*mtx['weight']
        mtx['vscore'] = mtx['vscore']*mtx['weight']

        mtx_ = mtx.groupby('hidx').vscore.sum().rename('htotalvote')

        mtx = mtx.groupby(['hidx', 'vidx'])[['hscore', 'vscore']].mean()
        mtx = pd.concat([mtx.reset_index().set_index('hidx'), mtx_], axis=1).reset_index()
        mtx['prob'] = mtx['vscore']/mtx['htotalvote']

        D = coo_matrix((mtx.prob.values, (mtx.hidx.values, mtx.vidx.values)), shape=(table.itemnum, table.itemnum)).transpose().tocsr()
        r = np.ones(table.itemnum)/table.itemnum
        pr = np.ones(table.itemnum)
        while norm(pr-r)>threshold:
            pr = r
            vrestart = restart*np.ones(table.itemnum)/table.itemnum
            r = (1-restart)*D.dot(r)+vrestart
            r /= np.sum(r)

        if hasattr(self, "rating"):
            self.rating["rating"] = r
        else:
            self.rating = pd.DataFrame({
                "iidx": np.arange(table.itemnum, dtype=np.int),
                "rating": r})
        return self._showcase(table, False)

class ODRanker(UnsupervisedRanker):
    """The Offence-defence rank tries to assign an offence rating and a defence rating to each team.
    By saying "offence rating", we assume that a team has a high offence rating when it gained a lot of points
    from a team good in defence. Vise versa. The offence rating of a team is associated with defence rating of each
    competitor in a non-linear way. The defence rating of a team is also non-linearly related to each competitors'
    offence rating.

    Parameters
    ----------
    method: {'summary', 'offence', 'defence'}, default 'summary'.
        The rating to be returned. 'summary' is offence/defence.
    epsilon: [0, +Inf) default 1e-4
        The small value that forces a convergence.
    threshold: (0, +Inf), default 1e-4
        The threshold that controls when the algorithm will converge.
    """
    def __init__(self, method='summary', epsilon=1e-4, threshold=1e-4):
        self.method = method
        self.epsilon = epsilon
        self.threshold = threshold

    def rank(self, table):
        """Calculate the rank and rating with specified parameters.

        Parameters
        ----------
        table: Table
            The record table to be ranked, should be a Table object.

        Returns
        -------
        pandas.DataFrame, with column ['name', 'rating', 'rank']
        """
        method, epsilon, threshold = self.method, self.epsilon, self.threshold
        mtx = pd.DataFrame(data={
            'hidx': pd.concat([table.table.hidx, table.table.vidx]),
            'vidx': pd.concat([table.table.vidx, table.table.hidx]),
            'hscore': pd.concat([table.table.hscore, table.table.vscore]),
            'vscore': pd.concat([table.table.vscore, table.table.hscore]),
            'weight': pd.concat([table.table.weight, table.table.weight])
        }, columns = ['hidx', 'vidx', 'hscore', 'vscore', 'weight']).reset_index(drop=True)
        mtx['hscore'] = mtx['hscore']*mtx['weight']
        mtx['vscore'] = mtx['vscore']*mtx['weight']
        mtx = mtx.groupby(['hidx', 'vidx'])[['hscore', 'vscore']].mean()
        mtx.reset_index(inplace=True)

        D = coo_matrix((mtx.vscore.values, (mtx.hidx.values, mtx.vidx.values)), shape=(table.itemnum, table.itemnum)).tocsr()
        Dt = D.transpose()

        prevd = np.ones(table.itemnum)/table.itemnum
        d = np.ones(table.itemnum)
        while norm(prevd-d)>threshold:
            prevd = d
            o = Dt.dot(1/d)+np.ones(d.shape[0])*epsilon*(np.sum(1/d))
            d = D.dot(1/o)+np.ones(o.shape[0])*epsilon*(np.sum(1/o))
        o = Dt.dot(1/d)

        if method=='summary':
            r = o/d
        elif method=='offence':
            r = o
        elif method=='defence':
            r = d
        else:
            raise ValueError('output should be one of summary, offence or defence.')
        if hasattr(self, "rating"):
            self.rating["rating"] = r
        else:
            self.rating = pd.DataFrame({
                "iidx": np.arange(table.itemnum, dtype=np.int),
                "rating": r})
        return self._showcase(table, True if method=='defence' else False)

class DifferenceRanker(UnsupervisedRanker):
    """This ranker targets at predicting score difference of games directly.
    The difference of ratings are proportional to the difference of score.
    """
    def rank(self, table):
        """Calculate the rank and rating with specified parameters.

        Parameters
        ----------
        table: Table
            The record table to be ranked, should be a Table object.

        Returns
        -------
        pandas.DataFrame, with column ['name', 'rating', 'rank']
        """
        mtx = pd.DataFrame(data={
            'hidx': pd.concat([table.table.hidx, table.table.vidx]),
            'vidx': pd.concat([table.table.vidx, table.table.hidx]),
            'hscore': pd.concat([table.table.hscore, table.table.vscore]),
            'vscore': pd.concat([table.table.vscore, table.table.hscore]),
            'weight': pd.concat([table.table.weight, table.table.weight])
        }, columns = ['hidx', 'vidx', 'hscore', 'vscore', 'weight']).reset_index(drop=True)
        mtx['score'] = mtx['hscore']-mtx['vscore']
        mtx['score'] = mtx['score']*mtx['weight']
        mtx = mtx.groupby(['hidx', 'vidx']).score.mean().reset_index()
        r = mtx.groupby('hidx').score.sum()/table.itemnum
        r = r.sort_index()

        if hasattr(self, "rating"):
            self.rating["rating"] = r.values
        else:
            self.rating = pd.DataFrame({
                "iidx": np.arange(table.itemnum, dtype=np.int),
                "rating": r})
        return self._showcase(table, False)

import numpy,from numpy,import scipy,from scipy


import numpy as np

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

class QDA_model:

    def fit(self, X, y):
        self.pi = np.sum(y==1)/len(y)
        self.mu1 = np.mean(X[y==1], axis=0)
        self.mu2 = np.mean(X[y==0], axis=0)

        sigma1 = np.cov(X[y==1].T)
        sigma2 = np.cov(X[y==0].T)
        self.sigma1_inv = np.linalg.inv(sigma1)
        self.sigma2_inv = np.linalg.inv(sigma2)

        self.a = np.log(self.pi/(1-self.pi)) + (1/2)*(self.mu2.T.dot(self.sigma2_inv).dot(self.mu2) -
                                            self.mu1.T.dot(self.sigma1_inv).dot(self.mu1))
        self.b = self.mu1.T.dot(self.sigma1_inv) - self.mu2.T.dot(self.sigma2_inv)

    def compute_frontier(self):

        def frontier(x1, x2):
            interm = np.concatenate((x1[:, :, np.newaxis], x2[:, :, np.newaxis]), axis=2).reshape(-1, 2)
            return self.predict_proba(interm).reshape(x1.shape)

        return frontier

    def predict_proba(self, X):
        return sigmoid(self.a + self.b.dot(X.T) +
                       (1/2)*np.sum(X.dot(self.sigma2_inv-self.sigma1_inv)*X, axis=1))

    def predict(self, X):
        return self.predict_proba(X) > 0.5

    def compute_misclassif_error(self, X, y):
        return np.sum(self.predict(X) != y)/len(y)


import numpy as np
import numpy.linalg as LA

# calculate ACF from l=0~l
def ACF(x, l):
    acf = np.zeros(l+1)
    for i in range(l+1):
        cov_matrix = np.cov(x[:len(x)-i], x[i:])
        acf[i] = cov_matrix[0, 1] / np.sqrt(cov_matrix[0, 0]*cov_matrix[1, 1])
    return acf

# the sample PACF is computed via the
# Durbin-Levinson recursive algorithm
def PACF(x, l, acf=None):
    if acf == None:
        acf = ACF(x, l)
    pacf = np.zeros([l+1, l+1])
    for k in range(1,l+1):
        pacf[k, k] = (acf[k] - np.dot(pacf[k-1, 1:k], acf[k-1:0:-1])) / \
                     (1 - np.dot(pacf[k-1, 1:k], acf[1:k]))
        for j in range(1,k):
            pacf[k, j] = pacf[k-1, j] - pacf[k, k]*pacf[k-1, k-j]

    return pacf

class AR:
    def __init__(self, p=0):
        self.p = p
        self.sigma_a = 0
        self.phi = None
        self.res = None

    def identifyP(self, x, method="PACF"):
        if method == "PACF":
            self.identifyP_PACF(x, epsilon=0.05)

    def identifyP_PACF(self, x, l=20, epsilon=0.05):
        pacf = PACF(x, l)
        for i in range(1, l+1):
            if np.abs(pacf[i, i]) < epsilon:
                self.p = i-1
                break

    def fit(self, x):
        """
        Just a linear regression using least square.
        """
        if self.p == 0: # if did not explicitly assign p
            self.identifyP(x)
        X = np.zeros([len(x)-self.p, self.p+1])
        Y = np.zeros([len(x)-self.p, 1])
        for i in range(len(x)-self.p):
            Y[i, 0] = x[i+self.p]
            X[i, 0] = 1
            X[i, 1:] = x[i:i+self.p] # Caution! Here the order of the input is not the order in the model
        self.phi = np.matmul(np.matmul(LA.inv(np.matmul(X.T, X)), X.T), Y)
        self.res = Y - np.matmul(X, self.phi)
        self.sigma_a = np.sqrt(np.sum(self.res*self.res)/(len(x)-2*self.p-1))

    def predict(self, x, step=1):
        X = np.zeros([1, self.p+1])
        X[0, 0] = 1
        if step == 1:
            return np.matmul(X, self.phi)
        else:
            pred = []
            for _ in range(step):
                pred.append(float(np.matmul(X, self.phi)))
                X[0, 1:-1] = X[0, 2:]
                X[0, -1] = pred[-1]
            return np.array(pred)

if __name__ == '__main__':
    data = np.genfromtxt(r"..\..\data\dgnp82.dat")

    ar3 = AR(3)
    ar3.fit(data)
    print(ar3.phi)
    print(ar3.sigma_a*ar3.sigma_a)
    print(ar3.predict(data, step=8))


from pygeom.geom3d import Vector
from sympy import Symbol, sqrt
from sympy import expand, simplify, trigsimp, expand_trig
from sympy import diff, integrate

class SymbolicVector(Vector):
    x = None
    y = None
    z = None
    def __init__(self, x: Symbol, y: Symbol, z: Symbol):
        super(SymbolicVector, self).__init__(x, y, z)
    def return_magnitude(self):
        return sqrt(self.x**2+self.y**2+self.z**2)
    def simplify(self):
        return SymbolicVector(simplify(self.x),
                              simplify(self.y),
                              simplify(self.z))
    def trigsimp(self):
        return SymbolicVector(trigsimp(self.x),
                              trigsimp(self.y),
                              trigsimp(self.z))
    def expand(self):
        return SymbolicVector(expand(self.x),
                              expand(self.y),
                              expand(self.z))
    def expand_trig(self):
        return SymbolicVector(expand_trig(self.x),
                              expand_trig(self.y),
                              expand_trig(self.z))
    def integrate(self, terms):
        return SymbolicVector(integrate(self.x, terms),
                              integrate(self.y, terms),
                              integrate(self.z, terms))
    def diff(self, sym: Symbol):
        return SymbolicVector(diff(self.x, sym),
                              diff(self.y, sym),
                              diff(self.z, sym))
    def subs(self, *args):
        return SymbolicVector(self.x.subs(*args),
                              self.y.subs(*args),
                              self.z.subs(*args))
    def to_unit(self):
        mag = self.return_magnitude()
        return SymbolicVector(self.x/mag, self.y/mag, self.z/mag)
    def __mul__(self, obj):
        if isinstance(obj, SymbolicVector):
            return self.x*obj.x+self.y*obj.y+self.z*obj.z
        else:
            x = obj*self.x
            y = obj*self.y
            z = obj*self.z
            return SymbolicVector(x, y, z)
    def __rmul__(self, obj):
        return self.__mul__(obj)
    def __truediv__(self, obj):
        x = self.x/obj
        y = self.y/obj
        z = self.z/obj
        return SymbolicVector(x, y, z)
    def __pow__(self, obj):
        if isinstance(obj, SymbolicVector):
            x = self.y*obj.z-self.z*obj.y
            y = self.z*obj.x-self.x*obj.z
            z = self.x*obj.y-self.y*obj.x
            return SymbolicVector(x, y, z)
    def __add__(self, obj):
        if isinstance(obj, SymbolicVector):
            x = self.x+obj.x
            y = self.y+obj.y
            z = self.z+obj.z
            return SymbolicVector(x, y, z)
    def __radd__(self, obj):
        if obj == 0 or obj is None:
            return self
        else:
            return self.__add__(obj)
    def __sub__(self, obj):
        if isinstance(obj, SymbolicVector):
            x = self.x-obj.x
            y = self.y-obj.y
            z = self.z-obj.z
            return SymbolicVector(x, y, z)
    def __pos__(self):
        return SymbolicVector(self.x, self.y, self.z)
    def __neg__(self):
        return SymbolicVector(-self.x, -self.y, -self.z)

def symple_vector(label, **kwargs):
    x = Symbol(f'{label:s}.x', **kwargs)
    y = Symbol(f'{label:s}.y', **kwargs)
    z = Symbol(f'{label:s}.z', **kwargs)
    return SymbolicVector(x, y, z)

ihat = SymbolicVector(1, 0, 0)
jhat = SymbolicVector(0, 1, 0)
khat = SymbolicVector(0, 0, 1)


# -----------------------------
# Author : Mehdi I.
# -----------------------------

import numpy as np

def get_total_number_of_spanning_trees(matrix,nodes_count):
    # STEP 1: Replace all the diagonal elements with the degree of nodes.
    for i in range(nodes_count):
        sum_of_col =0
        for j in range(nodes_count):
            sum_of_col+=matrix[j][i]
        matrix[i][i]=sum_of_col

    # STEP 2: Replace all non-diagonal 1’s with -1.
    for i in range(nodes_count):
        for j in range(nodes_count):
            if i!=j and matrix[i][j]==1:
                matrix[i][j]=-1

    # STEP 3: final result is co-factor of any element ( like 0,0 )
    matrix=np.delete(matrix, 0, 0)
    matrix=np.delete(matrix, 0, 1)

    return abs(round(np.linalg.det(matrix)))

if __name__ == '__main__':

    adjacency_matrix=[]

    # Specifying adjacency matrix of graph
    nodes_count=int(input("Enter your graph's nodes count : "))
    for i in range(nodes_count):
        row=[]
        print('Enter values for row '+str(i+1)+' of adjacency matrix : ')
        for j in range(nodes_count):
            row.append(int(input()))

        adjacency_matrix.append(row)

    print('\n Your Adjacency Matrix : \n')
    for row in adjacency_matrix:
        print(" ".join(map(str,row)))

    print('\n Total number of spanning trees for given graph is:', get_total_number_of_spanning_trees(adjacency_matrix,nodes_count))


# Numerical Dynamics in Three Dimensions

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

# Defining Variables
G = 6.67e-11
M = 6.42e23
R = 3.3895e6
m = 100

# simulation time, timestep and time
t_max = 100
dt = 0.1
t_array = np.arange(0, t_max, dt)

# initialise empty lists to record trajectories
position_list = []
velocity_list = []

#position_matrix = np.zeros(shape=(len(t_array),3))
#velocity_matrix = np.zeros(shape=(len(t_array),3))

# 3-element numpy matrices

position_list.append(np.array([0, 1e7, 0]))
velocity = np.array([np.sqrt((G*M)/np.linalg.norm(position_list[0])), 0, 0])
position = position_list[0] + dt*velocity + 0.5*dt*dt*(-G*M*position_list[0]/math.pow(np.linalg.norm(position_list[0]), 3))

"""

Scenario 1 : vx = 0

Scenario 2 : vx = np.sqrt((G*M)/np.linalg.norm(position_list[0]))

Scenario 3 : vx = 2500

Scenario 4 : vx = np.sqrt((2*G*M)/np.linalg.norm(position_list[0]))

"""

for t in t_array:
    for i in range ( 0, len(t_array) - 1 ) :
        if ( np.linalg.norm(position) >= R ) :

            position_list.append(position)
            velocity_list.append(velocity)

            # To calculate gravitational acceleration:
            acceleration = (-G*M*position/math.pow(np.linalg.norm(position), 3))

            # To do a Verlet update:
            position = 2*position - position_list[-2] + dt*dt*acceleration
            velocity = (0.5/dt) * (position - position_list[-2])

        else:
            break

position_y_final = []
position_x_final = []

for i in position_list :
    position_y_final.append(i[1])
    position_x_final.append(i[0])

# Scenario 1 : Straight Down Descent
# Set graph to altitude (y) against time
# Set vx variable to 0
"""
plt.figure(1)
plt.clf()
plt.xlabel('time (s)')
plt.grid()
plt.plot(t_array, position_y_final, label='altitude (m)')
plt.legend()
plt.show()

"""

# Scenario 2 : Circular Orbit
# Set graph to y-coordinate against x-coordinate
# Set vx variable to np.sqrt((G*M)/np.linalg.norm(position))

# Scenario 3 : Elliptical Orbit
# Set graph to y-coordinate against x-coordinate
# Set vx variable to anywhere in between Scenarios 2 and 4

# Scenario 4 : Hyperbolic Escape
# Set graph to y-coordinate against x-coordinate
# Set vx variable to np.sqrt((2*G*M)/np.linalg.norm(position))

plt.figure(2)
plt.clf()
plt.xlabel('y-coordinate (m)')
plt.grid()
plt.plot(position_y_final, position_x_final, label='x-coordinate (m)')
plt.legend()
plt.show()


from scipy.stats import multivariate_normal
import pandas as pd
import numpy as np

def generate_bank_customer_data(n_common=200, n_debtors=150, n_good=50, include_labels=True):
    """
    Function to generate bank customers data characterized by income and debt.
    :param int n_common: Number of common customers.
    :param int n_debtors: Number of debtors.
    :param int n_good: Number of good customers.
    :param bool include_labels: Whether to include or not ground truth labels.
    :return: pd.DataFrame with generated data.
    """
    # Parameters
    mu1 = np.array([3, 2.5])
    mu2 = np.array([2, 6])
    mu3 = np.array([6, 5])
    cov1 = np.array([[1.2, -0.2],
                     [-0.2, 1]])
    cov2 = np.array([[0.6, 0],
                     [0, 0.6]])
    cov3 = np.array([[1, 0.9],
                     [0.9, 1]])

    # Ground truth random variables
    p1 = multivariate_normal(mean=mu1, cov=cov1)
    p2 = multivariate_normal(mean=mu2, cov=cov2)
    p3 = multivariate_normal(mean=mu3, cov=cov3)

    # Data generation
    data = np.concatenate([p1.rvs(size=n_common),
                           p2.rvs(size=n_debtors),
                           p3.rvs(size=n_good)],
                          axis=0)
    data = pd.DataFrame({"income": data[:, 0], "debt": data[:, 1]})

    # Include ground truth labels
    if include_labels:
        data["labels"] = np.concatenate([np.zeros((n_common, 1)),
                                         np.ones((n_debtors, 1)),
                                         2 * np.ones((n_good, 1))],
                                        axis=0)

    return data.sample(frac=1).reset_index(drop=True)


import numpy as np

class LinearRegression(object):
	def __init__(self, x = None, y = None):
		super(LinearRegression, self).__init__()
		# Data.
		# X E R(mxn) m=data points ; n=features.
		# Y E R(mx1) m=data points ; single column.
		self.x = x
		self.y = y
		# Weights. W X R(nx1) n=features ; single column.
		self.w = self.initializeWeights(n = x.shape[1])
		# Hyperparameters
		self.epochs = 500
		self.learningRate = 0.01

	def initializeWeights(self, n = None, seed = None):
		if (seed != None):
			np.random.seed(seed)
		return np.random.rand(n).reshape(-1, 1)

	def costFunction(self, hwx = None, deriv = None):
		# Local variables.
		m = self.x.shape[0]
		# Logic
		if (deriv == True):
			jw = (1/m) * np.dot(self.x.T, (hwx - self.y))
		else:
			jw = (1/(2*m)) * np.sum((hwx - self.y)**2)
		return jw

	def train(self):
		# BGD
		for e in range(self.epochs):
			hwx = np.dot(self.x, self.w)
			self.w = self.w - (self.learningRate * self.costFunction(hwx = hwx, deriv = True))
			if (e%10 == 0):
				jw = self.costFunction(hwx = hwx, deriv = False)
				print("INFO: Cost function: ", jw)


import numpy as np
import modern_robotics as mr
import math

print("Ex 1: Determina the end-effector zero configuration M")
M = np.array([[1, 0, 0, 3.73],
              [0, 1, 0,    0],
              [0, 0, 1, 2.73],
              [0, 0, 0,    1]])
print(M)
# [[1,0,0,3.73],[0,1,0,0],[0,0,1,2.73],[0,0,0,1]]

print("Ex 2: Determine the screw axes Si")
Ss = np.array([[ 0, 0,    0,     0, 0,     0],
               [ 0, 1,    1,     1, 0,     0],
               [ 1, 0,    0,     0, 0,     1],
               [ 0, 0,    1, -0.73, 0,     0],
               [-1, 0,    0,     0, 0, -3.73],
               [ 0, 1, 2.73,  3.73, 1,     0]])
print(Ss)
# [[0,0,0,0,0,0],[0,1,1,1,0,0],[1,0,0,0,0,1],[0,0,1,-0.73,0,0],[-1,0,0,0,0,-3.73],[0,1,2.73,3.73,1,0]]

print("Ex 3: Determine the screw aes Bi")
Bs = np.array([[   0,     0,    0, 0, 0, 0],
               [   0,     1,    1, 1, 0, 0],
               [   1,     0,    0, 0, 0, 1],
               [   0,  2.73, 3.73, 2, 0, 0],
               [2.73,     0,    0, 0, 0, 0],
               [   0, -2.73,   -1, 0, 1, 0]])
print(Bs)
# [[0,0,0,0,0,0],[0,1,1,1,0,0],[1,0,0,0,0,1],[0,2.73,3.73,2,0,0],[2.73,0,0,0,0,0],[0,-2.73,-1,0,1,0]]

print("Ex 4: Find the end-effector configuration T using FKinSpace")
theta = np.array([-math.pi/2, math.pi/2, math.pi/3, -math.pi/4, 1, math.pi/6])
T_space = np.around(mr.FKinSpace(M, Ss, theta), 2)
print(T_space)
# [[0.5,0.87,0,1],[0.22,-0.13,-0.97,-1.9],[-0.84,0.48,-0.26,-4.5],[0,0,0,1]]

print("Ex 5: Find the end-effector configuration T using FKinBody")
T_body = np.around(mr.FKinBody(M, Bs, theta), 2)
print(T_body)
# [[0.5,0.87,0,1],[0.22,-0.13,-0.97,-1.9],[-0.84,0.48,-0.26,-4.5,],[0,0,0,1]]


import numpy as np

# Inner (or dot) product
a = np.array([1,2])
b = np.array([3,4])
np.inner(a, b)
a.dot(b)

# Outer product
a = np.array([1,2])
b = np.array([3,4])
np.outer(a, b)

# Inverse
m = np.array([[1,2], [3,4]])
np.linalg.inv(m)

# Inner (or dot) product
m = np.array([[1,2], [3,4]])
minv = np.linalg.inv(m)
m.dot(minv)

# Diagonal
m = np.array([[1,2], [3,4]])
np.diag(m)

m = np.array([1,2])
np.diag(m)

# Determinant
m = np.array([[1,2], [3,4]])
np.linalg.det(m)

# Trace - sum of elements of the diagonal
m = np.array([[1,2], [3,4]])
np.diag(m)
np.diag(m).sum()
np.trace(m)

# Transpose
m = np.array([ [1,2], [3,4] ])
m.T

# Gaussian distribution
m = np.random.randn(2,3)
m

# Covariance
X = np.random.randn(100,3)
np.cov(X.T)

# Eigen vectors and values
# For symmetric matrix (m == m.T) and hermitian matrix (m = m.H) we use eigh.
m = np.array([
  [ 0.89761228,  0.00538701, -0.03229084],
  [ 0.00538701,  1.04860676, -0.25001666],
  [-0.03229084, -0.25001666,  0.81116126]])

# The first tuple contains three Eigen values.
# The second tuple contains Eigen vectors stored in columns.
np.linalg.eigh(m)

# Solving linear systems
# The admissions fee at a small far is $1.50 for children an $4.00 for adults.
# On a certain day 2,200 people enter the fair and $5050 is collected.
# How many children and how many adults attended.
#
# Let X1 = number of children
# Let X2 = number of adults
# X1 + X2 = 2200
# 1.5X1 + 4X2 = 5050
a = np.array([ [1,1], [1.5,4] ])
b = np.array( [ 2200, 5050] )
np.linalg.solve(a, b)


# -*- coding: utf8 -*-
import numpy as np
import numpy.linalg as la

# Datos iniciales
k1 = 1000.0
k2 = 2000.0
k3 = 3000.0
P = 5000.0

# Matrices por elemento
K1 = np.array([[k1,-k1],[-k1,k1]])
K2 = np.array([[k2,-k2],[-k2,k2]])
K3 = np.array([[k3,-k3],[-k3,k3]])

# Matriz global
K = np.array([[  K1[0,0],        0,           K1[0,1],                0],
               [      0 ,  K3[0,0],                 0,          K3[0,1]],
               [ K1[1,0],        0,   K1[1,1]+K2[0,0],          K2[0,1]],
               [       0,  K3[1,0],           K2[1,0],  K2[1,1]+K3[1,1]]])

F = np.array([0, 0, 0, P])

# Condiciones de frontera
# Nodos 1 y 2 conocidos -> UX = 0
KS = K[2:,2:]
FS = F[2:]

# Resolviendo
USOL = la.solve(KS, FS)

# Vector de desplazamientos
USOL = np.concatenate(([0,0],USOL))

# Obteniendo las fuerzas nodales
NF = np.dot(K,USOL)

# Presentando los resultados
for nodo in range(4):
    print("%g  UX = %-8.4f    FX = %-8.4f"%(nodo+1, USOL[nodo], NF[nodo]))


"""

Functions related to STAAR

"""

import numpy as np
from scipy.stats import cauchy

c = cauchy()

def cct(pvals, weights=None):
    """
    Python port of the CCT function as defined in the STAAR R-package (https://github.com/xihaoli/STAAR/blob/2f67fafec591a45e81a54eca24564b09ce90e252/R/CCT.R)

    An analytical p-value combination method using the Cauchy distribution.

    takes in a numeric vector of p-values, a numeric vector of non-negative weights, and return the aggregated p-value using Cauchy method.

    :param np.ndarray pval: Numpy array containing the p-values
    :param np.ndarray weights: Numpy array containing the weights

    :return: The aggregated p-value
    :rtype: float

    Liu, Y., & Xie, J. (2020). Cauchy combination test: a powerful test with analytic p-value calculation under arbitrary dependency structures.
    Liu, Y., et al. (2019). Acat: A fast and powerful p value combination method for rare-variant analysis in sequencing studies.
    """

    # check for NA
    assert not np.isnan(pvals).any(), 'Error: Cannot have nan in the p-values!'
    # check range
    assert not (((pvals < 0).sum() + (pvals > 1).sum()) > 0), "Error: All p-values must be between 0 and 1"

    # check for p-values that are either exactly 0 or 1
    is_zero = (pvals == 0.).any()
    is_one = (pvals == 1.).any()

    assert not (is_zero & is_one), 'Error: Cannot have both 0 and 1 p-values'

    if is_zero:
        return 0
    if is_one:
        print('Warning: there are p-values that are exactly 1!')
        return 1.

    # check the validity of weights
    if weights is None:
        weights = np.ones_like(pvals) / len(pvals)
    else:
        assert len(weights) == len(pvals), 'Error: length of weights should be the same as that of the p-values!'
        assert not ((weights < 0).any()), 'Error: all weights must be positive!'
        weights /= weights.sum()

    # check if there are very small non-zero p-values
    is_small = pvals < 1e-16
    if not is_small.any():
        cct_stat = (weights * np.tan((0.5 - pvals) * np.pi)).sum()
    else:
        cct_stat = (weights[is_small] / pvals[is_small] / np.pi).sum()
        cct_stat += (weights[~is_small] * np.tan((0.5 - pvals[~is_small]) * np.pi)).sum()

    # check if the test statistic is very large
    if (cct_stat > 1e15):
        pval = (1. / cct_stat) / np.pi
    else:
        pval = c.sf(cct_stat)

    return pval


import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import minimize

class ModelPredictiveControl:
    def __init__(self):
        self.horizon = 40

    def plant_model(self, u, prev_temp):
        knob_angle = u # control input
        # Knob angle to temperature
        knob_temp = knob_angle * 0.5
        # Calculate dT or change in temperature.
        tau = 6
        dT = (knob_temp - prev_temp) / tau
        # new temp = current temp + change in temp.
        return prev_temp + dT

    def cost_function(self, u):
        cost = 0.0
        temp = 0.0 #initial temp
        ideal_temp = 40
        #within a fixed horizon
        for i in range(0, self.horizon):
            temp = self.plant_model(u[i], temp)
            cost += abs(ideal_temp - temp)

        return cost #cost summation in a fixed horizon

mpc = ModelPredictiveControl()

# Set bounds, or constraints
bounds = []
for i in range(mpc.horizon):
    bounds += [[0, 180]] #hard constraint

# Create Inputs to be filled.
u = np.ones(mpc.horizon)

# Non-linear optimization.
u_solution = minimize(mpc.cost_function,
                      x0=u,
                      method='SLSQP',
                      bounds=bounds,
                      tol = 1e-8) #threshold

# # --------------------------
# # Calculate data for Plot 1, which has constant control input: knob angle
# knob_angle_list = []
# water_temp_list = []
# t_list = []
# knob_angle = 80
# water_temp = 0.0
# for t in range(40):
#     t_list += [t]
#     knob_angle_list += [knob_angle]
#     water_temp_list += [water_temp]
#     water_temp = mpc.plant_model(knob_angle, water_temp)

# # Create Plot 1 - Constant Input
# # Subplot 1
# plt.figure(figsize=(8,8))
# plt.subplot(211)
# plt.title("Constant Input")
# plt.ylabel("Knob Angle")
# # Enter Data
# plt.plot(t_list, knob_angle_list, 'k')
# plt.ylim(0,180)

# # Subplot 2
# plt.subplot(212)
# plt.ylabel("Water Temp")
# # Enter Data
# plt.plot(t_list, water_temp_list, 'ro')
# plt.ylim(0,50)
# plt.show()

# --------------------------
# Calculate data for Plot 2.
knob_angle_list = []
water_temp_list = []
t_list = []
water_temp = 0.0
for t in range(40):
    t_list += [t]
    knob_angle = u_solution.x[t] # get the sequence of control inputs that minimizes cost
    knob_angle_list += [knob_angle]
    water_temp_list += [water_temp]
    water_temp = mpc.plant_model(knob_angle, water_temp)

# Plot 2 - MPC
# Subplot 1
plt.figure(figsize=(8,8))
plt.subplot(211)
plt.title("MPC")
plt.ylabel("Knob Angle")
# Enter data
plt.plot(t_list, knob_angle_list, 'k')
plt.ylim(0,180)

# Subplot 2
plt.subplot(212)
plt.ylabel("Water Temp")
# Enter data
plt.plot(t_list, water_temp_list, 'ro')
plt.ylim(0,50)
plt.show()


import numpy as np

class Householder:
  """
  Classe que representa o algoritmo utilizado para fazer transformações de Householder
  para reduzir uma matriz simétrica a uma tridiagonal simetrica semelhante.

  Args:
      A (numpy.ndarray): Array n por n que representa a matriz completa a ser reduzida.
      print_steps (bool, optional): Indica se deve ou não imprimir os passos intermediários. Padrão é False.

  Attributes:
    n (int): Variável que indica a dimensão da matriz do sistema.
    k (int): Guarda o número da iteração atual do algorítmo.
    print_steps (bool): Indica se deve imprimir informações de cada iteração na tela.
    A (numpy.ndarray): Guarda o valor atual da matriz A.
    Ht (numpy.ndarray): Guarda o valor atual da matriz H transposta.
  """
  def __init__(self, A, print_steps=False):
    assert A.shape[0] == A.shape[1]

    self.n = A.shape[0]
    self.k = 0
    self.print_steps = print_steps

    self.A = np.ndarray(A.shape, dtype=float)

    self.Ht = np.identity(self.n, dtype=float)

    np.copyto(self.A, A)

  def _get_alfa_beta(self):
    """
    Retorna a diagonal pricipal e subdiagonal da matriz tridiagonalizada pelo algoritmo.

    Returns:
        dict: Dicionário com dois elementos, um numpy.ndarray representando a diagonal principal
                cuja chave é "alfa", e um representando as subdiagonais, com chave "beta".
    """
    alfa = np.ndarray(self.n, dtype=float)
    beta = np.ndarray(self.n-1, dtype=float)

    for i in range(self.n-1):
      alfa[i] = self.A[i][i]
      beta[i] = self.A[i+1][i]

    alfa[self.n-1] = self.A[self.n-1][self.n-1]

    result = {
      'alfa' : alfa,
      'beta' : beta,
    }

    return result

  def _iterate_once(self):
    """
    Método que representa uma iteração de transformações de Householder para uma matriz simétrica.
    """
    if self.print_steps:
      print(f'Iteração {self.k+1}:')

    # Calcula o valor do wk da iteracao atual
    ak_ = self.A[self.k+1 :, [self.k]]

    wk_ = np.copy(ak_)
    wk_[0] += np.sign(ak_[0]) * np.linalg.norm(ak_)

    if self.print_steps:
      print(f'w{self.k+1}_:\n{wk_}')

    # Multiplica a matriz H_wk a esquerda de A
    for i in range(self.k, self.n):
      ai_ = self.A[self.k+1 :, [i]]

      self.A[self.k+1 :, [i]] = ai_ - 2 * np.dot(ai_.T, wk_) / np.dot(wk_.T, wk_) * wk_

    if self.print_steps:
      print(f'H_w{self.k+1} A:\n{self.A}')

    # Multiplica a matriz H_wk a direita de A
    for i in range(self.k+1, self.n):
      bi_ = self.A[[i], self.k+1 :]

      self.A[[i], self.k+1 :] = bi_ - 2 * np.dot(bi_, wk_) / np.dot(wk_.T, wk_) * wk_.T

    if self.print_steps:
      print(f'H_w{self.k+1} A H_w{self.k+1}:\n{self.A}')

    # Multiplica a matriz H_wk a direita de Ht
    for i in range(0, self.n):
      bi_ = self.Ht[[i], self.k+1 :]

      self.Ht[[i], self.k+1 :] = bi_ - 2 * np.dot(bi_, wk_) / np.dot(wk_.T, wk_) * wk_.T

    if self.print_steps:
      print(f'Ht H_w{self.k+1}:\n{self.Ht}\n')

    self.k += 1

  def iterate(self):
    """
    Método que executa as transformações de Householder para a matriz fornecida no construtor.

    Returns:
        dict: Dicionário contendo a matriz tridiagonal e a matriz tranposta de H, para serem
                utilizados pelo método QR.
    """
    for m in range(self.n-2):
      self._iterate_once()

    result = {}
    result['T'] = self._get_alfa_beta()
    result['Ht'] = self.Ht

    return result

if __name__ == '__main__':
  print('Esse arquivo contem apenas o algoritmo Householder, mas não os testes.')
  print('Para rodar os testes corretamente, por favor consulte o LEIAME.txt')


from collections import defaultdict
import numpy as np

class DGraph():
    def __init__(self, num_vertices):
        self.num_vertices = num_vertices
        self.graph = defaultdict(list)
        self.tc_graph = defaultdict(list)
        # Transitive closure.
        self.tc = np.zeros((num_vertices, num_vertices))

    def add_edge(self, u, v):
        self.graph[u].append(v)

    def dfs_traverse(self, s, v):
        self.tc[s][v] = 1
        self.tc_graph[s].append(v)

        for i in self.graph[v]:
            if self.tc[s][i] < 1:
                self.dfs_traverse(s, i)

    def transitive_closure(self):
        for i in range(self.num_vertices):
            self.dfs_traverse(i, i)

def transitive_closure(g):
    '''
    Parameters:
        g (dict), a DAG with adjacent list representation
    Return:
        trans_path(dict), where key = vertice v, value = a dict maps reachable
        nodes u to a path of v to u
        trans_dict(dict), the adjacent list representation of the transitive
        closure of g
    '''
    trans_path = {}
    trans_dict = {}
    for v in g:
        transitive_closure_helper(g, trans_path, v)

    for v in trans_path:
        reachables = []
        reachable_dict = trans_path[v]
        for u in reachable_dict:
            reachables.append(u)
        trans_dict[v] = reachables
    return trans_path, trans_dict

def transitive_closure_helper(g, trans_path, v):
    '''
    Helper function of the 'transitive_closure'
    Parameters:
        g (dict), a DAG with adjacent list representation
        trans_path(dict), where key = vertice v, value = a dict maps reachable
        nodes u to a path of v to u
        v: a vertex v
    Return:
        trans_path(dict)
    '''
    if v in trans_path:
        return trans_path
    if len(g[v]) == 0:
        trans_path[v] = {}
        return trans_path
    nbrs = g[v]
    v_dict = {}
    for u in nbrs:
        recur = transitive_closure_helper(g, trans_path, u)
        u_dict = recur[u]
        for node in u_dict:
            if node not in v_dict:
                # TODO: O(n), will fix later to amortized O(1).
                v_dict[node] = [v] + u_dict[node]
        v_dict[u] = [v, u]
    trans_path[v] = v_dict
    return trans_path

def tst():
    g = DGraph(4)
    g.add_edge(0, 1)
    g.add_edge(0, 2)
    g.add_edge(1, 2)
    g.add_edge(2, 0)
    g.add_edge(2, 3)
    g.add_edge(3, 3)
    g.transitive_closure()
    print(g.tc)

if __name__ == "__main__":
    print("Running tests for transitive closure.")
    tst()

# References
# [1] https://www.geeksforgeeks.org/transitive-closure-of-a-graph-using-dfs/
# [2] https://www.cs.princeton.edu/courses/archive/spr03/cs226/lectures/digraph.4up.pdf


import numpy as np
import matplotlib.pyplot as plt

def hoocneNhan(A,xk):
    A.append(1)
    for i in range(len(A)-2,0,-1):
        A[i] = A[i - 1] - A[i] * xk
    A[0] = - A[0] * xk
    return A

def hoocneChia(A,xk):
    B = np.ones(len(A) - 1)
    for i in range(len(B) - 2,-1,-1):
        B[i] = A[i + 1] + B[i + 1] * xk
    return B

def init():
    global x,y,n
    x = []
    y = []
    with open('nsLagrange.txt','r+') as f:
            for line in f.readlines():
                xt = float(line.split(' ')[0])
                yt = float(line.split(' ')[1])
                check = True
                for x_check in x:
                    if x_check == xt:
                        check = False
                        break
                if check:
                    x.append(xt)
                    y.append(yt)
                plt.scatter(xt,yt)
    x = np.asarray(x)
    y = np.asarray(y)
    n = len(x)

def PolyCoefficients(xt, coeffs):
    """ Returns a polynomial for ``x`` values for the ``coeffs`` provided.

    The coefficients must be in ascending order (``x**0`` to ``x**o``).
    """
    o = len(coeffs)
    yt = 0
    for i in range(o):
        yt += coeffs[i] * xt ** i
    return yt

def main():
    init()

    # tinh D
    D = []
    for i in range(n):
        D.append(1)
        for j in range(n):
            if(i != j):
                D[i] *= (x[i] - x[j])
    D = np.asarray(D)

    # tinh w
    w = [1]
    for i in range(n):
        w = hoocneNhan(w,x[i])
    w = np.asarray(w)

    # tinh wi
    wi = []
    for i in range(n):
        wi.append(hoocneChia(w,x[i]))
    wi = np.asarray(wi)

    # tinh px
    px = np.zeros(n)
    for i in range(n):
        for j in range(n):
            px[i] += wi[j,i] * y[j] / D[j]

    print(px)

    plt.scatter(x,y)

    xt = np.linspace(min(x)-0.5,max(x) + 0.5,1000)
    plt.plot(xt,PolyCoefficients(xt,px))
    plt.savefig("mygraph.png")

    #plt.show()

if __name__=='__main__':
    main()

Topic 3 - Function Approximation/16.Lagrange_Interpolation/nsLagrange.py


import numpy as np
import scipy.signal

def discount(x, gamma):
    """
    computes discounted sums along 0th dimension of x.

    inputs
    ------
    x: ndarray
    gamma: float

    outputs
    -------
    y: ndarray with same shape as x, satisfying

        y[t] = x[t] + gamma*x[t+1] + gamma^2*x[t+2] + ... + gamma^k x[t+k],
                where k = len(x) - t - 1

    """
    assert x.ndim >= 1
    return scipy.signal.lfilter([1],[1,-gamma],x[::-1], axis=0)[::-1]

def explained_variance(ypred,y):
    """
    Computes fraction of variance that ypred explains about y.
    Returns 1 - Var[y-ypred] / Var[y]

    interpretation:
        ev=0  =>  might as well have predicted zero
        ev=1  =>  perfect prediction
        ev<0  =>  worse than just predicting zero

    """
    ##assert y.ndim == 1 and ypred.ndim == 1
    vary = np.var(y)
    return np.nan if vary==0 else 1 - np.var(y-ypred)/vary

def explained_variance_2d(ypred, y):
    assert y.ndim == 2 and ypred.ndim == 2
    vary = np.var(y, axis=0)
    out = 1 - np.var(y-ypred)/vary
    out[vary < 1e-10] = 0
    return out

def ncc(ypred, y):
    return np.corrcoef(ypred, y)[1,0]

def flatten_arrays(arrs):
    return np.concatenate([arr.flat for arr in arrs])

def unflatten_vector(vec, shapes):
    i=0
    arrs = []
    for shape in shapes:
        size = np.prod(shape)
        arr = vec[i:i+size].reshape(shape)
        arrs.append(arr)
        i += size
    return arrs

def discount_with_boundaries(X, New, gamma):
    """
    X: 2d array of floats, time x features
    New: 2d array of bools, indicating when a new episode has started
    """
    Y = np.zeros_like(X)
    T = X.shape[0]
    Y[T-1] = X[T-1]
    for t in range(T-2, -1, -1):
        Y[t] = X[t] + gamma * Y[t+1] * (1 - New[t+1])
    return Y

def test_discount_with_boundaries():
    gamma=0.9
    x = np.array([1.0, 2.0, 3.0, 4.0], 'float32')
    starts = [1.0, 0.0, 0.0, 1.0]
    y = discount_with_boundaries(x, starts, gamma)
    assert np.allclose(y, [
        1 + gamma * 2 + gamma**2 * 3,
        2 + gamma * 3,
        3,
        4
    ])


import numpy as np
from matplotlib import pyplot as plt

# 一维
a = np.array([1, 2, 3]); print(a) # [1 2 3]
# 等间隔数字的数组
b = np.arange(10); print(b) # [0 1 2 3 4 5 6 7 8 9]
b = np.arange(3,7,2); print(b) # array([3, 5])
# 二维
c = np.array([[1, 2], [3, 4]]); print(c) # [[1 2]
										 #  [3 4]]

# 调整数组shape
a = np.array([[1, 2, 3], [4, 5, 6]]); print(a); # [[1 2 3]
												#  [4 5 6]]
a.shape = (3, 2); print(a)                      # [[1 2]
												#  [3 4]
												#  [5 6]]

# ndim:返回数组的维数
a = np.arange(24);
print(a)
print(a.ndim) # 1

# numpy.reshape: 在不改变数据的条件下修改形状
b = a.reshape(2, 4, 3); print(b.ndim) # 3

# 空数组
x = np.empty([3, 2], dtype='i1'); print(x) # 数组x的元素为随机值，因为它们未初始化

# 含有5个0的数组，若不指定类型，则默认为float
x = np.zeros(5, dtype=np.int); print(x) # [0 0 0 0 0]
# 含有6个1的二维数组，若不指定类型，则默认为float
x = np.ones([2, 3], dtype=int); print(x) # [[1 1 1]
										 #  [1 1 1]]

# 使用内置的range()函数创建列表对象
x = range(5); print(x) # range(0, 5)

# numpy.linspace，此函数类似于arange，在此函数中，指定了范围之间的均匀间隔数量，而不是步长
x = np.linspace(10, 20, 5); print(x) # [10. 12.5 15. 17.5  20.]

# 算数运算：add, subtract, multiply, divide, reciprocal, power, mod 输入数组必须具有相同的形状或符合数组广播规则
a, b = [5, 6], [7, 10]
c = np.subtract(a, b); print(c) # [-2 -4]

# 统计函数：用于从数组中给定的元素中查找最小，最大，百分标准差和方差等, amin, amax, ptp, percentile, median, mean, average, std
a = np.array([1, 2, 3, 4, 5])
print(np.amin(a)) # 1
print(np.mean(a)) # 3.0


"""
Laboratory Experiment 3 - Script
 - Rootlocus project
 @author Rafael Lima
"""

from sympy import *

def simplifyFraction(G,s):
    """
    Expand numerator and denominator from given fraction
    """
    num,den = fraction(G.expand().simplify())
    num = Poly(num,s)
    den = Poly(den,s)

    return (num/den)

def partfrac(G,s):
    """
    Split Fraction into several factors using residues theorem
    """
    # Find Poles
    poles = solve(sympy.fraction(G.expand().simplify())[1],s)

    # Find Resudues
    Gp = 0
    for p in poles:
        Gp = Gp + (G*(s-p)).subs(s,p)/(s-p)

    return Gp

def roundExpr(expr, num_digits=4):
    """
    Round Every Number in an expression
    """
    return expr.xreplace({n : round(n, num_digits) for n in expr.atoms(Number)})

printing.printer.Printer().set_global_settings(precision=3)

# Symbols
s = symbols("s",complex=True)
z = symbols("z",complex=True)
K,a1,a0,T,b = symbols("K alpha_1 alpha_0 T beta",real=True)

# Constants
na0 = 1
nT = 0.5

# Open Loop Transfer Function
sGo = 1/(s+na0)

# Z Transform (From table)
sGz = (1/na0)*(1-exp(-T))/(z-exp(-T))

# Controler TF
sGc = K*z/(z-1)
sGma = simplify(expand(sGc*sGz))
sGmf = simplify(expand(sGma/(1+sGma)))

# Characterist Equation
_,poly = fraction(sGmf)

# Find Critical Value for K
sK = solve(poly,K)[0]
Kmax = sK.subs([(T,nT),(z,-1)])

poles2 = solve(poly.subs([(T,nT),(K,2)]),z)

# Part 2
# Constants
na1 = 2
Ts = 0.2

# Open Loop Transfer Function
sGo2 = 1/(s+2)

# TODO find Z Transform (From table)
# BUG: not matching ZOH discretization from matlab
sGz2 = (z-1)*(1/(4*(z-exp(-2*T))) - 1/(4*(z-1)) + T*(1/((z-1)*(z-1))))

# Controler TF
sGc2 = K*(z-exp(-na1*T))/(z-b)
sGma2 = simplifyFraction(sGc2*sGz2,z)
sGmf2 = simplify(expand(sGma2/(1+sGma2)))

# Expression from matlab
mGz2 = ((2533546664982251*z)/144115188075855872 + 554410548014771/36028797018963968)/(z**2 - (3761226368457787*z)/2251799813685248 + 6037706219090157/9007199254740992)

# Controler TF from Matlab
mGc2 = K*(z-exp(-na1*T))/(z-b)
mGma2 = simplifyFraction(mGc2*mGz2,z)
mGmf2 = simplify(expand(mGma2/(1+mGma2)))

# Characterist Equation
_,poly2 = fraction(sGmf2)

# Request Conditions
desiredDamping = 0.5
desiredSettlingTime = 2
desiredOvershoot = exp(-desiredDamping*pi/sqrt(1-desiredDamping**2))
desiredPoles = [0,0]
desiredPoles[0] = -(4/desiredSettlingTime)*(1 + I*sqrt(1-desiredDamping**2)/desiredDamping)
desiredPoles[1] = -(4/desiredSettlingTime)*(1 - I*sqrt(1-desiredDamping**2)/desiredDamping)
desiredPolesZ = [exp(desiredPoles[0]*Ts),exp(desiredPoles[1]*Ts)]

# Solve Linear System to find K and b
sysKb = [K,b]
sysKb[0] = poly2.subs([(z,desiredPolesZ[0]),(T,Ts)]).evalf().collect(K).collect(b)
sysKb[1] = poly2.subs([(z,desiredPolesZ[1]),(T,Ts)]).evalf().collect(K).collect(b)
resp = list(linsolve(sysKb,(K,b)))[0]
nK = resp[0]
nb = resp[1]

# Find TF
nGmf2 = sGmf2.subs([(K,nK),(b,nb),(T,Ts)])

# Find Critical Value for K
sK2 = solve(poly2,K)[0]
#Kmax = sK.subs([(T,nT),(z,-1)])


# %%
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
from matplotlib.animation import FuncAnimation
from matplotlib.collections import PolyCollection
# matplotlib parameters to ensure correctness of Chinese characters
plt.rcParams["font.family"] = 'sans-serif'
plt.rcParams['font.sans-serif']=['Arial Unicode MS', 'SimHei'] # Chinese font
plt.rcParams['axes.unicode_minus']=False # correct minus sign

plt.rcParams["font.size"] = 16
plt.rcParams["xtick.labelsize"] = 16
plt.rcParams["ytick.labelsize"] = 16
plt.rcParams["axes.spines.top"] = False
plt.rcParams["axes.spines.right"] = False
#%%
prefix = 'Hypothesis_movie'
class UpdateFigure:
    def __init__(self,
        ax:plt.Axes, dx:float=0.05):
        """Plot the first frame for the animation.

        Args:
            ax (plt.Axes): axes of flight icons.
        """

        self.ax = ax
        self.ax.spines["left"].set_visible(False)
        self.dx = dx
        self.mean = 7.0
        self.sigma = 0.04
        self.th = 0.0784
        self.x = np.linspace(-0.25,0.25,1000) + self.mean

        self.ax.set_xlim(self.x[0],self.x[-1])
        self.ax.set_ylim(0, 12.0)
        self.norm = norm(loc=self.mean, scale=self.sigma)
        self.gauss = self.norm.pdf
        self.isf = self.norm.isf(0.025)
        self.ppf = self.norm.ppf(0.025)
        self.ax.set_yticks([])

        # start ploting
        plt.savefig(prefix+'_step1.pdf')
        self.line = self.ax.plot(self.x,self.gauss(self.x), color='k', zorder=1)
        plt.savefig(prefix+'_step2.pdf')
        self.ax.axvline(self.mean, ymax=0.95, ls='--', color='#C00000')

        vline0 = self.ax.axvline(norm.isf(0.025, loc=self.mean, scale=self.sigma), ymax=0.8, ls='--', color='#002060')
        vline1 = self.ax.axvline(norm.ppf(0.025, loc=self.mean, scale=self.sigma), ymax=0.8, ls='--', color='#002060')
        self.vlines = [vline0, vline1]
        plt.savefig(prefix+'_step3.pdf')

        shade_verts = [
            self._shading_verts(self.isf, 'right'),
            self._shading_verts(self.ppf, 'left'),
        ]
        self.shades = [
            PolyCollection(verts, fc='#0096FE', alpha=0.8, zorder=0)
            for verts in shade_verts
        ]
        [self.ax.add_collection(shade) for shade in self.shades]
        stick0, text0 = self.create_text(self.isf, 'right')
        stick1, text1 = self.create_text(self.ppf, 'left')
        self.texts = [text0, text1]
        self.sticks = [stick0, stick1]
        plt.savefig(prefix+'_step4.pdf')

    def create_text(self, x, direction='right'):
        height = 1.2
        width = 0.06
        offset = 0.005
        if direction == 'right':
            stick, = self.ax.plot(x+np.array([offset, offset+width]),self.gauss(x+offset)/2+np.array([0., height]), color='k', zorder=1)
            text = self.ax.text(x+offset+0.005+width, height+self.gauss(x+offset)/2, r'$\frac{\alpha}{2}$', ha='left')
        elif direction == 'left':
            stick, = self.ax.plot(x-np.array([offset, offset+width]),self.gauss(x-offset)/2+np.array([0., height]), color='k', zorder=1)
            text = self.ax.text(x-offset-0.005-width, height+self.gauss(x-offset)/2, r'$\frac{\alpha}{2}$', ha='right')
        return stick, text

    def update_text(self, x, stick, text, direction='right'):
        height = 1.2
        width = 0.06
        offset = 0.005
        if direction == 'right':
            stick.set_data(x+np.array([offset, offset+width]),self.gauss(x+offset)/2+np.array([0., height]))
            text.set_position((x+offset+0.005+width, height+self.gauss(x+offset)/2))
        elif direction == 'left':
            stick.set_data(x-np.array([offset, offset+width]),self.gauss(x-offset)/2+np.array([0., height]))
            text.set_position((x-offset-0.005-width, height+self.gauss(x-offset)/2))
        return stick, text

    def update_gauss(self):
        self.line[0].set_data(self.x,self.gauss(self.x))

    def update_vlines(self):
        [self.vlines[i].set_data(np.ones(2)*val, [0,0.8]) for i, val in enumerate((self.isf, self.ppf))]

    def _shading_verts(self, x, direction='right'):
        if direction == 'right':
            _x = self.x[self.x>=x]
            _y = self.gauss(self.x[self.x>=x])
        elif direction == 'left':
            _x = self.x[self.x<=x]
            _y = self.gauss(self.x[self.x<=x])
        verts = [[_xx, _yy] for _xx, _yy in zip(_x, _y)]
        verts += [[_x[-1], 0],[_x[0], 0]]
        return np.array([verts])

    # def __call__(self, i):
    #     # This way the plot can continuously run and we just keep
    #     # watching new realizations of the process
    #     depsilon=1.5e-4
    #     eps = 0.025-i*depsilon
    #     self.isf = self.norm.isf(eps)
    #     self.ppf = self.norm.ppf(eps)
    #     self.update_vlines()
    #     shade_verts = [
    #         self._shading_verts(self.isf, 'right'),
    #         self._shading_verts(self.ppf, 'left' ),
    #     ]
    #     [shade.set_verts(verts) for shade, verts in zip(self.shades, shade_verts)]
    #     self.update_text(self.isf, self.sticks[0], self.texts[0], 'right')
    #     self.update_text(self.ppf, self.sticks[1], self.texts[1], 'left')
    #     return self.line

    def __call__(self, i):
        # This way the plot can continuously run and we just keep
        # watching new realizations of the process
        self.norm = norm(loc=self.mean, scale=self.sigma-i*self.dx)
        self.gauss = self.norm.pdf
        self.isf = self.norm.isf(0.025)
        self.ppf = self.norm.ppf(0.025)
        self.update_gauss()
        self.update_vlines()
        shade_verts = [
            self._shading_verts(self.isf, 'right'),
            self._shading_verts(self.ppf, 'left'),
        ]
        [shade.set_verts(verts) for shade, verts in zip(self.shades, shade_verts)]
        self.update_text(self.isf, self.sticks[0], self.texts[0], 'right')
        self.update_text(self.ppf, self.sticks[1], self.texts[1], 'left')
        return self.line
fig, ax = plt.subplots(1,1, figsize=(10,3),dpi=400)

# create a figure updater
ud = UpdateFigure(ax, dx=0.00014)
# user FuncAnimation to generate frames of animation
anim = FuncAnimation(fig, ud, frames=121, blit=True)
# save animation as *.mp4
anim.save('hypothesis_movie.mp4', fps=60, dpi=400, codec='libx264', bitrate=-1, extra_args=['-pix_fmt', 'yuv420p'])
# %%


#!/usr/bin/env python3
from scipy.interpolate import CubicSpline
import numpy as np

x = np.arange(21)*np.pi/5.0
y = np.sin(x)
y[0] = 0.01
y[-1] = y[0]
xq = np.linspace(min(x), max(x), 101)
cs_not_a_knot = CubicSpline(x, y, bc_type='not-a-knot')
cs_clamped    = CubicSpline(x, y, bc_type='clamped')
cs_natural    = CubicSpline(x, y, bc_type='natural')
cs_periodic   = CubicSpline(x, y, bc_type='periodic')
yq_knot    = cs_not_a_knot(xq)
yq_clamped = cs_clamped(xq)
yq_natural = cs_natural(xq)
yq_period  = cs_periodic(xq)
ofl = open('cubicSplineInterpolation.txt', 'w')
for i in range(len(xq)):
    ofl.write("%e, %e, %e, %e, %e\n"%(xq[i], yq_knot[i], yq_clamped[i],
                                      yq_natural[i], yq_period[i]))
ofl.close()
print(cs_not_a_knot.integrate(0, 1))
print(cs_natural.integrate(max(x), 0))
print(cs_clamped.integrate(4.08, 6))
print(cs_periodic.integrate(3.99, max(x)))
print(cs_periodic.integrate(3.007729,2.541008))


import math
import numpy as np
#from fastdist import fastdist

class Proximity:
    def __init__(self):
        pass

    @staticmethod
    def compute(a, b, proxtype):
        p = 0.0;
        # euclidean
        if proxtype=="euclidean":
            for i in range(len(a)):
                c = a[i]-b[i]
                c = c**2
                p+=c

            # p = math.sqrt(p)
            p = np.sqrt(p)

            # a = np.array(a)
            # b = np.array(b)
            # d = [(ai - bi)**2 for ai, bi in zip(a, b)]
            # p = math.sqrt(sum(d))

        return p

    # @staticmethod
    # def computeFE(XFEi=None, XFEj=None, i=None, j=None, proxtype):
    #     p = 0.0;
    #     # euclidean
    #     if proxtype=="euclidean":
    #         #pi = ProximityMatrix.getProximityType()["Euclidean"];
    #         p = XFEi.proximity_two(XFEi, XFEj, i, j, 0)

    #     return p

class PMatrix:
    def __init__(self, n):
        self.dist = [ [] for i in range(n)]
        for i in range(n):
            self.dist[i] = [ None for j in range(n)];

    def get(self, i, j):
        mi = i
        mj = j
        if j<i:
            mi = j
            mj = i

        return self.dist[mi][mj]

    def set(self, i, j, d):
        mi = i
        mj = j
        if j<i and i!=j:
            mi = j
            mj = i
        self.dist[mi][mj] = d


# -*- coding: utf-8 -*-
"""
Created on Sat Jan 30 21:39:29 2021

@author: TaeKen
"""
from math import sin, cos, sqrt, atan2, radians, degrees
import numpy as np

def distance(loc1, loc2):

    R = 3958.8

    lat1 = radians(loc1[0])
    long1 = radians(loc1[1])
    lat2 = radians(loc2[0])
    long2 = radians(loc2[1])

    dlong = long2 - long1
    dlat = lat2 - lat1

    a = sin(dlat / 2)**2 + cos(lat1) * cos(lat2) * sin(dlong / 2)**2
    c = 2 * atan2(sqrt(a), sqrt(1 - a))

    distance = R * c

    return distance

def midpoint(loc1, loc2,pop1=1,pop2=1):

    lat1 = radians(loc1[0])
    long1 = radians(loc1[1])
    lat2 = radians(loc2[0])
    long2 = radians(loc2[1])

    x = (cos(lat1) * cos(long1) *pop1 + cos(lat2) * cos(long2) *pop2)/(pop1+pop2)
    y = (cos(lat1) * sin(long1) *pop1 + cos(lat2) * sin(long2) *pop2)/(pop1+pop2)
    z = (sin(lat1) *pop1 + sin(lat2) *pop2)/(pop1+pop2)

    central_longitude = atan2(y, x)
    central_square_root = sqrt(x * x + y * y)
    central_latitude = atan2(z, central_square_root)

    return np.array([degrees(central_latitude),degrees(central_longitude)])


from scipy.stats import entropy
from scipy.special import rel_entr
import numpy as np

def ent(l):
    return entropy(l, base=2)

# relative entropy or Kullback-Leibler Divergence
# id not a distance metric because it is not symmetric
def rel_ent(p, q, e=1e-15):
    assert len(p) == len(q)
    pn = [i / sum(p) for i in p]
    qn = [i / sum(q) for i in q]

    return sum(
        pn[i] * np.log2(np.clip(pn[i], e, 1.0) / np.clip(qn[i], e, 1.0))
        for i in range(len(pn))
    )

def js_div(p, q):
    assert len(p) == len(q)
    pn = [i / sum(p) for i in p]
    qn = [i / sum(q) for i in q]

    m = 0.5 * (np.array(pn) + np.array(qn))
    return 0.5 * rel_ent(pn, m) + 0.5 * rel_ent(qn, m)


# Play with Kalman filters from Kalman-and-Bayesian-Filters-in-Python
# Press the green button in the gutter to run the script.
# install packages using 'python -m pip install numpy, matplotlib, scipy, pyfilter
# References:
#   [2] Roger Labbe. "Kalman and Bayesian Filters in Python"  -->kf_book
#   https://github.com/rlabbe/Kalman-and-Bayesian-Filters-in-Pythonimport numpy as np
# Dependencies:  python3.10, numpy, matplotlib, math, filterpy, ./kf_book
from numpy.random import randn
import numpy as np
import matplotlib.pyplot as plt
import math
from kf_book.nonlinear_plots import plot_gaussians
from filterpy.kalman import KalmanFilter
from filterpy.common import Q_discrete_white_noise
from math import sqrt

def univariate_filter(x0, P, R, Q):
    kf1 = KalmanFilter(dim_x=1, dim_z=1, dim_u=1)
    kf1.x = np.array([[x0]])
    kf1.P *= P
    kf1.H = np.array([[1.]])
    kf1.F = np.array([[1.]])
    kf1.B = np.array([[1.]])
    kf1.Q *= Q
    kf1.R *= R
    return kf1

def pos_vel_filter(x, P, R, Q=0., dt=1.0):
    """ Returns a KalmanFilter which implements a
    constant velocity model for a state [x dx].T
    """

    kf = KalmanFilter(dim_x=2, dim_z=1)
    kf.x = np.array([x[0], x[1]]) # location and velocity
    kf.F = np.array([[1., dt],
                     [0.,  1.]])  # state transition matrix
    kf.H = np.array([[1., 0]])    # Measurement function
    kf.R *= R                     # measurement uncertainty
    if np.isscalar(P):
        kf.P *= P                 # covariance matrix
    else:
        kf.P[:] = P               # [:] makes deep copy
    if np.isscalar(Q):
        kf.Q = Q_discrete_white_noise(dim=2, dt=dt, var=Q)
    else:
        kf.Q[:] = Q
    return kf

def plot_1d_2d(xs, xs1d, xs2d):
    plt.plot(xs1d, label='1D Filter')
    plt.scatter(range(len(xs2d)), xs2d, c='r', alpha=0.7, label='2D Filter')
    plt.plot(xs, ls='--', color='k', lw=1, label='track')
    plt.title('State')
    plt.legend(loc=4)
    plt.show()

def compare_1D_2D(x0, P, R, Q, vel, u=None):
    # storage for filter output
    xs, xs1, xs2 = [], [], []

    # 1d KalmanFilter
    f1D = univariate_filter(x0, P, R, Q)

    #2D Kalman filter
    f2D = pos_vel_filter(x=(x0, vel), P=P, R=R, Q=0)
    if np.isscalar(u):
        u = [u]
    pos = 0 # true position
    for i in range(100):
        pos += vel
        xs.append(pos)

        # control input u - discussed below
        f1D.predict(u=u)
        f2D.predict()

        z = pos + randn()*sqrt(R) # measurement
        f1D.update(z)
        f2D.update(z)

        xs1.append(f1D.x[0])
        xs2.append(f2D.x[0])
    plt.figure()
    plot_1d_2d(xs, xs1, xs2)
    print('example2:  done')

if __name__ == '__main__':
    compare_1D_2D(x0=0, P=50., R=5., Q=.02, vel=1.)


# load the data for time-series
import numpy as np
from scipy import signal

from load_time_series import load_data

np.random.seed(231)

x = np.array([1, 2, 3, 4])
# print("train_set_x[0]: ", x)
print("len of x: ", len(x))

filter_size = 2
corr_filter = np.array([1, 2])

standard_corr = signal.correlate(x, corr_filter, 'valid')
print("len of standard corr: ", len(standard_corr))

# print("standard_corr:", standard_corr)

def fft_cross_correlation(x, output_len, preserve_energy_rate=0.95):
    xfft = np.fft.fft(x)
    squared_abs = np.abs(xfft) ** 2
    full_energy = np.sum(squared_abs)
    current_energy = 0.0
    preserve_energy = full_energy * preserve_energy_rate
    index = 0
    while current_energy < preserve_energy and index < len(squared_abs):
        current_energy += squared_abs[index]
        index += 1
    xfft = xfft[:index]
    filterfft = np.conj(np.fft.fft(corr_filter, len(xfft)))
    # element-wise multiplication in the frequency domain
    out = xfft * filterfft
    # take the inverse of the output from the frequency domain and return the modules of the complex numbers
    out = np.fft.ifft(out)
    output = np.array(out, np.double)
    # output = np.absolute(out)

    output = output[:output_len]
    return output

# print("output of cross-correlation via fft: ", output)
output = fft_cross_correlation(x, len(standard_corr), preserve_energy_rate=0.95)
print("is the fft cross_correlation correct: ", np.allclose(output, standard_corr, atol=1e-12))
print("absolute error: ", np.sum(np.abs(output - standard_corr)))


"""
This module stores all the utility functions
that are required for the implementation of
of any Machine Learning logic but not associated
to any particular technique.
"""

import numpy as np

def euclidean_distance(data_points):
    """
    It calculates euclidean distance between two
    set of points.

    :param data_points: np.narray for two set of points
    :return: Euclidean Distance
    """
    assert len(data_points) == 2
    # Sum of Squared distance as in Euclidean distance
    return np.sqrt(np.sum((data_points[0] - data_points[1]) ** 2))


import numpy as np
import scipy.io
import matplotlib.pyplot as plt
import random

def generate_heat_equation_data(amplitude=1.0, n=1, phase=0):
    """
    @param amplitude: maximum temperature of initial condition
    @param n: [int] no of harmonics of wave of initial condition
    @param phase: phase shift of the initial condition
    @return: numpy array
    """

    L=1             # length of rod in cm
    max_t = 2       # maximum time steps in seconds
    c = 0.03       # intrinsic property of metal used - fixed for same type of metal
    exponential_const = (c * n * np.pi / L) ** 2

    t = np.linspace(0, max_t, 200)
    x = np.linspace(0, L, 200)
    X, T = np.meshgrid(x, t)
    # X = X.reshape(-1,1)
    # T = T.reshape(-1,1)
    U = amplitude*(np.exp(-exponential_const*T))*np.sin((n * np.pi * X / L) + phase)
    U = U.reshape(200,200)

    # [DEBUG]
    # to see results
    # Domain bounds
    lb = np.array([0, 0.0])
    ub = np.array([L, max_t])
    def show_plot():
        plt.imshow(U.T, interpolation='nearest', cmap='YlGnBu',
                   extent=[lb[1], ub[1], lb[0], ub[0]],
                   origin='lower', aspect='auto')

        plt.ylabel('x (cm)')
        plt.xlabel('t (seconds)')
        plt.axis()
        plt.colorbar().set_label('Temperature (°C)')
        plt.show()
    # show_plot()
    return U.T

def generate_dataset():
    heat_dataset = np.zeros((1100, 200, 200))
    cnt = 0
    for amplitude in range(11):
        for harmonics in range(1,11):
            for phase_shift in range(10):
                phase_shift = random.random() * 2 * np.pi
                heat_dataset[cnt] = generate_heat_equation_data(amplitude=random.random(), n=harmonics, phase=phase_shift)
                cnt += 1
    np.random.shuffle(heat_dataset)
    return heat_dataset

if __name__ == '__main__':
    # generate range of data of heat equation
    dataset = generate_dataset()
    # save the matrix
    scipy.io.savemat('data/heat_N1100_T200_r200.mat', mdict={ 'u': dataset})


from functools import reduce
from hw1.ex12 import solve_ex1

import numpy as np
import random

def solve_diagonal_system(system, result):
    assert len(system.shape) == 2
    assert system.shape[0] == system.shape[1], "Must be a square matrix"

    solution = np.zeros((system.shape[1], 1))
    lines, columns = system.shape
    for idx in reversed(range(0, lines)):
        line_offset = reduce(lambda accumulator, comb: accumulator + comb[0] * comb[1],
                             zip(system[idx, idx + 1:], solution[idx + 1:, 0]),
                             0)

        solution[idx, 0] = (result[idx, 0] - line_offset) / system[idx][idx]

    return solution

def reduce_system(system, result, column=0):
    if column == system.shape[1]:
        return

    idx = column + system[column:, column].argmax()
    system[[column, idx]] = system[[idx, column]]
    result[[column, idx]] = result[[idx, column]]

    for line in range(column + 1, system.shape[0]):
        epsilon = solve_ex1()
        if -epsilon <= system[line, column] <= epsilon:
            continue

        normalization_factor = system[column, column] / system[line, column]
        system[line] *= normalization_factor
        system[line] -= system[column]

        result[line, 0] *= normalization_factor
        result[line, 0] -= result[column, 0]

    reduce_system(system, result, column=column + 1)

def solve_system(system, result):
    internal_system = np.copy(system)
    internal_result = np.copy(result)

    reduce_system(internal_system, internal_result)
    return solve_diagonal_system(internal_system, internal_result)

def generate_random_system(size):
    system = []
    for i in range(0, size):
        current_line = []
        for j in range(0, size):
            current_line.append(random.random() * 10)
        system.append(current_line)

    result = []
    for i in range(0, size):
        result.append([random.random() * 10])
    return np.array(system), result

def flip(vali_list):
    result = []
    for x in vali_list:
        result.append(x[0])
    return result

if __name__ == '__main__':
    sys_result_pair = generate_random_system(100)

    system = sys_result_pair[0]
    result = sys_result_pair[1]

    try:
        determinant = np.linalg.det(system)
        if determinant == 0:
            raise ValueError("Error: Determinant is 0")
    except ValueError as error:
        print(repr(error))
        exit(1)

    solution = solve_system(
        system,
        result
    )

    np_solution = np.linalg.solve(system, result)

    """
    print("Solution =", solution)
    print("NP Solution =", np_solution)
    """

    solution_norm = solution - np_solution
    print("Norma solutia noastra - solutia biblioteca =", np.linalg.norm(solution_norm))

    solution_mul_sys = np.matmul(system, solution) - result
    print("Norma solutia noastra * sistemul - rezultatul =", np.linalg.norm(solution_mul_sys))

    np_solution_mul_sys = np.matmul(system, np_solution) - result
    print("Norma solutia biblioteca * sistemul - rezultatul =", np.linalg.norm(np_solution_mul_sys))
    """
    solution_result = np.matmul(system, solution)
    print("A * x_sol =", solution_result)
    print("Result = ", result)
    print("Norm: ", np.linalg.norm(result - solution_result))
    """


import numpy as np
from tqdm import tqdm
from numpy import linalg as LA

class KernelBalancing:
    def __init__(self,
                 X,
                 A,
                 Y,
                 _lambda
                 ):
        self.X = X
        self.A = A
        self.Y = Y
        self._lambda = _lambda
        self.N = X.shape[0]
        self.M = Y.shape[0]
        self.KXX = np.zeros((self.N,self.N))
        self.KXY = np.zeros((self.N,self.M))
        self.KYY = np.zeros((self.M,self.M))
    def K(self,
          x,
          y):
        """
        x,y : np.array
        """
        return np.exp(-LA.norm(x - y)**2*.5)
        #return LA.norm(x - y)**2

    def getGramMatrix(self,
                      KXX = None,
                      KXY = None,
                      KYY = None):
        if KXX==None:
            print("Calculating Gram Matrix...")
            for i in range(self.N):
                for j in range(self.N):
                    self.KXX[i,j] = self.K(self.X[i],self.X[j])
            for i in range(self.N):
                for j in range(self.M):
                    self.KXY[i,j] = self.K(self.X[i],self.Y[j])
            for i in range(self.M):
                for j in range(self.M):
                    self.KYY[i,j] = self.K(self.Y[i],self.Y[j])
    def H(self,
          W):
        return -2.*self.A.dot(self.KXY).dot(W[:-1]) + W[:-1].dot(self.KYY).dot(W[:-1]) + self._lambda*np.sum(W[:-1]**2)

    def nabla_k(self,
                k,
                W):
        k = int(k)
        return -2.*np.dot(np.transpose(self.A),self.KXY[:,k]) + 2.*self._lambda  * W[k] + 2.*np.sum(self.KYY[k,:]*W[:-1]) + 2.*(np.sum(W[:-1])-1.)*W[-1]*W[k]

    def nabla_H(self, W):
        gradH = np.zeros(self.M+1)
        for i in range(self.M):
            gradH[i] = self.nabla_k(i,W)
        gradH[-1] = 1. - np.sum(W[:-1])
        return gradH
    def getOptimalWeights(self,
                          learning_rate = 0.001,
                          max_iter = 1000,
                          epsilon = 1e-7):
        W = np.zeros(self.M+1)
        W[:-1] = np.zeros(self.M) + 1./self.M
        W[-1] = 1.
        H_trace = []
        H_old = self.H(W)
        for i in tqdm(range(max_iter)):
            H_trace += [H_old]
            W -= learning_rate* self.nabla_H(W = W)
            for d in range(self.M+1):
                W[d] = np.max([W[d],0.])

            H_new = self.H(W)
            if  np.abs(H_new- H_old) <= epsilon:
                print("The preset tolorance is reached.")
                break
            H_old = H_new
        print("final objective value:", H_new)
        return W[:-1], np.array(H_trace)


# Matplotlib Plotting Tutorials
# Plot Animation
# 1D Heat equation

import numpy as np
import matplotlib.pyplot as plt

L = 10                          # Length of rod
nx = 100                        # number of points on the rod
T = 500                         # Total time for simulation
nt = 500                        # number of time steps
alpha = 5e-3                    # Diffusion Coefficient

x = np.linspace(0, L, nx+1)     # points on the rod
dx = x[1] - x[0]                # Spacing between points
t = np.linspace(0, T, nt+1)     # instances in time
dt = t[1] - t[0]                # time step
F = alpha*dt/dx**2              # diffusion parameter
fac = 1.0 - 2.0*F               # constant in the equation

# Set initial condition: a sine wave
Temp_init = 100*np.sin(2*np.pi*x/L)

Temp_old = np.copy(Temp_init)   # known T at old time level
Temp_new = np.zeros(nx+1)       # unknown T at the present time level

# To enable interactive plotting
plt.ion()
plt.figure()

for n in range(0, nt):
    print('time t=',t[n])
    # Compute temperature at all the points
    Temp_new[1:-1] = fac*Temp_old[1:-1] + F*(Temp_old[0:-2] + Temp_old[2:])

    # Update temperature before next step
    Temp_old[:]= Temp_new

    plt.clf()  # Clear the figure
    plt.plot(x, Temp_init, color='xkcd:black', label='Initial Temperature profile')
    plt.plot(x, Temp_old, color='xkcd:salmon', label='Current Temperature profile')  # Contour plotting the values
    plt.xlabel('Length', fontsize=10)  # X axis label
    plt.ylabel('Temperature in $^o C$', fontsize=10)  # Y axis label
    plt.title('Temperature Diffusion in a 1D rod')
    # string = " ".join(['Time $t$ = ', str(t[n]), ' s, $T_{max}$ = ', str(np.amax(Temp_old)), ' $^o C$, $T_{min}$ = ', str(np.amin(Temp_old)), '$^o C$'])
    string = 'Time $t$ = '+str(t[n])+' s, $T_{max}$ = '+str(np.amax(Temp_old))+' $^o C$, $T_{min}$ = '+str(np.amin(Temp_old))+'$^o C$'
    plt.suptitle(string)
    plt.legend()
    plt.grid(True)  # Enabling gridding
    plt.axis((-0.1, 10.1, -105, 105))  # Making axis rigid
    plt.pause(0.01)


import numpy as np
import matplotlib.pyplot as plt

mu = 0.0
sigma = 1.

x = np.linspace(-3.0, 3.0, 100)
y = np.exp(-((x - mu) ** 2) / (2 * sigma ** 2)) / np.sqrt(2 * np.pi * sigma ** 2)

plt.plot(x,y,color='black')
plt.fill_between(x, y, np.zeros_like(y),color='#add8e6')
plt.tick_params(bottom=False, left=False, right=False, top=False)
plt.tick_params(labelbottom=False, labelleft=False, labelright=False, labeltop=False)
plt.gca().spines["right"].set_visible(False)
plt.gca().spines["top"].set_visible(False)
plt.gca().spines["left"].set_visible(False)
plt.ylim(0.,0.5)
# plt.show()
# plt.savefig('gaussian.pdf')
plt.savefig('gaussian.svg')


import numpy as np
import matplotlib.pyplot as plt
from astropy.visualization import astropy_mpl_style
from scipy.special import factorial
plt.style.use(astropy_mpl_style)

def thermal(n, n_bar):
  return ((n_bar/(1+nbar)) ** n / (1 + n_bar))

def coherent(n, n_bar):
  return (np.exp(-n_bar) * n_bar ** n / factorial(n))

ns = np.arange(0, 40)
nbars = [6]
width = 0.35
alpha = .8

for nbar in nbars:
  plt.bar(ns, coherent(ns, nbar), label='coherent ' + str(nbar), alpha = alpha, width=width)
  plt.bar(ns+width, thermal(ns, nbar), label='thermal ' + str(nbar), alpha = alpha, width=width)
plt.legend()
plt.show()

def moment(b, v, n):
  m = np.average(b, weights=v)
  if (n == 1):
    return(m)
  return (np.sum((b - m)**n * v) / np.sum(v))

def analyze(dist, ns, nbar):
  bins = dist(ns, nbar)
  moments = {'mean': 1, 'variance': 2, 'skewness': 3, 'kurtosis': 4}
  for m, num in moments.items():
    print(m, ' = ', moment(ns, bins, num))

"""
For a Poissonian (coherent): mean=variance=skewness=nu
and kurtosis = nu*(1+3*nu)

For the thermal distribution: nth moment is something like exp(n)
"""

import numpy,from scipy,import astropy,from astropy


# Compute pi value by Monte Carlo sampling
# circle area within a square with unit length
# pi = Area / r^2
# Area = Area of the square * number of points within circle / total number of points
# Area of the square = 4 * r^2
# pi = 4.0 * number of points within circle / total number of points
import random
import numpy as np
import math

# number of points for sampling
N = 100000
r = 1.0
r2 = 1.0 * 1.0
A = 0
ii = 10000
for i in range(ii):
    xy = np.random.rand(2, N)
    xy2 = np.square(xy)
    A += np.sum(np.add(xy2[0,:], xy2[1,:]) < r2)
    pi = 4.0 * A / N / (i + 1)
    print "{0} samples, calculated pi value = {1} with error {2} compared to math.pi {3}".format(N * (i + 1), pi, abs(pi - math.pi), math.pi)
#pi = 4.0 * A / N / ii
#print "{0} samples, calculated pi value = {1} with error {2} compared to math.pi {3}".format(N * ii, pi, abs(pi - math.pi), math.pi)


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

class Regres:
    def load_dataset(self,filename)->(np.matrix,np.matrix):
        fr = io.open(filename)
        features = []
        lables = []
        for line in fr.readlines():
            line_split = line.strip().split('\t')
            line_attr = []
            for i_attr in range(len(line_split)-1):
                line_attr.append(float(line_split[i_attr]))
            features.append(line_attr)
            lables.append(float(line_split[-1]))
        return np.asmatrix(features),np.asmatrix(lables).reshape(-1,1)

    def stand_regres(self,features:np.matrix,lables:np.matrix):
        xTx = features.T * features
        if np.linalg.det(xTx) == 0.0:
            print("the matrix is singular,can not be inverse")
            return
        ws = xTx.I * (features.T * lables)
        return  ws

    def local_weight_Linear_regres(self,test_point,features:np.matrix,lables:np.matrix,k=1.0):
        sample_count,feature_count = np.shape(features)
        local_weight = np.asmatrix(np.eye(sample_count))
        k_square = k ** 2
        for j in range(sample_count):
            diff_mat = test_point - features[j,:]
            local_weight[j,j] = np.exp((diff_mat*diff_mat.T)/(-2.0*k_square))
        xTx = features.T * (local_weight * features)
        if np.linalg.det(xTx) == 0.0:
            print("the matrix is singular,can not be inverse")
            return
        ws = xTx.I * (features.T *(local_weight* lables))
        return test_point*ws

    def rigde_regres(self,features:np.matrix,lables:np.matrix,lamda=0.2):
        sample_count,feature_count = np.shape(features)
        xTx = features.T * features
        denom = np.eye(feature_count) * lamda
        denom = xTx + denom
        if np.linalg.det(denom) == 0.0:
            print("the matrix is singular,can not be inverse")
            return
        return  denom.I * (features.T * lables)

    def state_wise(self,features:np.matrix,lables:np.matrix,eps=0.01,num_itor = 100):
        label_mean = np.mean(lables, 0)
        lables = lables - label_mean
        features = regularize(features)
        sample_count,feature_count = np.shape(features)
        return_mat = np.zeros((num_itor,feature_count))
        ws = np.zeros((feature_count,1))
        ws_best = ws.copy()
        for i in range(num_itor):
            lowest_error = np.inf
            for j in range(feature_count):
                for sign in [-1,1]:
                    ws_test = ws.copy()
                    ws_test[j] += sign*eps
                    label_pred = features * ws_test
                    error =rss_error(lables.T, label_pred.T)
                    if error < lowest_error:
                        lowest_error = error
                        ws_best = ws_test
            ws = ws_best.copy()
            return_mat[i,:]= ws.T
        return return_mat

def rss_error(label,label_pred):
    error = np.asarray(label-label_pred)
    return (error**2).sum()

def regularize(features_raw:np.matrix ):
    features_mean = np.mean(features_raw,0)
    features_var = np.var(features_raw,0)
    features = (features_raw - features_mean)/features_var
    return  features

def test_stand_regres():
    regres = Regres()
    features, lables = regres.load_dataset("data/ch08/ex0.txt")
    weights = regres.stand_regres(features, lables)
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(features[:,1].flatten().getA()[0],lables[:,0].flatten().getA()[0])

    features_sort:np.matrix = features.copy()
    features_sort.sort(0)
    lables_predit = features_sort * weights
    ax.plot(features_sort[:,1].flatten().getA()[0],lables_predit[:,0].flatten().getA()[0])

    plt.show()

def test_lwl_regres(k=1.0):
    regres = Regres()
    features, lables = regres.load_dataset("data/ch08/ex0.txt")
    sample_count,feature_count = np.shape(features)
    labels_predit = np.zeros(sample_count)
    for i in range(sample_count):
        labels_predit[i] = regres.local_weight_Linear_regres(features[i],features,lables,k)

    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.scatter(features[:, 1].flatten().getA()[0], lables[:, 0].flatten().getA()[0])

    sordInd = features[:,1].argsort(0)
    features_sorted = features[sordInd][:,0,:]
    #print(np.shape(features_sorted))
    #print(features_sorted)
    ax.plot(features_sorted[:, 1].flatten().getA()[0], labels_predit[sordInd])

    fig.show()

def test_ridge():
    regres = Regres()
    features_raw, lables_raw = regres.load_dataset("data/ch08/abalone.txt")
    label_mean = np.mean(lables_raw,0)
    lables = lables_raw - label_mean
    features_mean = np.mean(features_raw,0)
    features_var = np.var(features_raw,0)
    features = (features_raw - features_mean)/features_var
    num_test_pts = 30
    sample_count,feature_count = np.shape(features)
    w_mat = np.zeros((num_test_pts,feature_count))
    for i in range(num_test_pts):
        ws = regres.rigde_regres(features,lables,np.exp(i-10))
        w_mat[i,:] = ws.T
    print(w_mat)
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.plot(w_mat)
    plt.show()

def test_stage_wise():
    regres = Regres()
    features_raw, lables_raw = regres.load_dataset("data/ch08/abalone.txt")
    w_mat = regres.state_wise(features_raw,lables_raw,0.001,5000)
    #print(w_mat)
    fig = plt.figure()
    ax = fig.add_subplot(111)
    ax.plot(w_mat)
    plt.show()

if __name__ == '__main__':
    #test_stand_regres()
    #m = np.asmatrix([[4,3],[2,10],[5,6]])
    #print(np.shape(m))
    #m.sort(1)
    #print(m)
    #test_lwl_regres(0.002)
    #test_ridge()
    #test_stage_wise()
    print(np.sqrt(5/8))