import numpy as np

# Note: please don't import any new package. You should solve this problem using only the package(s) above.
#-------------------------------------------------------------------------

'''
    Problem 1: Linear Regression (Maximum Likelihood)
    In this problem, you will implement the linear regression method based upon maximum likelihood (least square).
    -------------------------
    Package(s) to Install:
        Please install python version 3.7 or above and the following package(s):
        * nose (for unit tests)
        * numpy (for n-dimensional arrays)
    How to Install:
        * nose: To install 'nose' using pip, you could type in the terminal:
            pip3 install nose
        * numpy: To install 'numpy' using pip, you could type in the terminal:
            pip3 install numpy
    -------------------------
    A list of all variables being used in this problem is provided at the end of this file.
'''

#--------------------------
def Terms_and_Conditions():
    '''
        By submitting this homework or changing this function, you agree with the following terms:
       (1) Not sharing your code/solution with any student before and after the homework due. For example, sending your code segment to another student, putting your solution online or lending your laptop (if your laptop contains your solution or your Dropbox automatically copied your solution from your desktop computer and your laptop) to another student to work on this homework will violate this term.
       (2) Not using anyone's code in this homework and building your own solution. For example, using some code segments from another student or online resources due to any reason (like too busy recently) will violate this term. Changing other's code as your solution (such as changing the variable names) will also violate this term.
       (3) When discussing with any other student about this homework, only discuss high-level ideas or use pseudo-code. Don't discuss about the solution at the code level. For example, two students discuss about the solution of a function (which needs 5 lines of code to solve) and they then work on the solution "independently", however the code of the two solutions are exactly the same, or only with minor differences (variable names are different). In this case, the two students violate this term.
      All violations of (1),(2) or (3) will be handled in accordance with the WPI Academic Honesty Policy.  For more details, please visit: https://www.wpi.edu/about/policies/academic-integrity/dishonesty
      Note: we may use the Stanford Moss system to check your code for code similarity. https://theory.stanford.edu/~aiken/moss/
      Historical Data: in one year, we ended up finding 25% of the students in that class violating this term in their homework submissions and we handled ALL of these violations according to the WPI Academic Honesty Policy.
    '''
    #*******************************************
    # CHANGE HERE: if you have read and agree with the term above, change "False" to "True".
    Read_and_Agree = True
    #*******************************************
    return Read_and_Agree

#---------------------------------------------------
'''
    Fit a linear model on training samples. Compute the parameter w using Maximum likelihood (equal to least square).
    Inputs:
        * X: the feature matrix of the training samples, a numpy matrix of shape n by p.
        * y: the sample labels, a numpy vector of length n.
    Outputs:
        * w: the weights of the linear regression model, a numpy float vector of length p.
    Hints:
        * You could use np.linalg.inv() to compute the inverse of a matrix.
        * You could use @ operator in numpy for matrix multiplication: A@B represents the matrix multiplication between matrices A and B.
        * You could use A.T to compute the transpose of matrix A.
        * This problem can be solved using 1 line(s) of code.
'''
#---------------------
def least_square(X, y):
    #########################################
    ## INSERT YOUR CODE HERE
    w = np.linalg.inv(X.T@X)@X.T@y
    #########################################
    return w
#---------------------
    '''
        TEST: Now you can test the correctness of your code above by typing the following in the terminal:
        ---------------------------------------------------
        nosetests -v test1.py:test_least_square
        ---------------------------------------------------
    '''

#---------------------------------------------------
'''
    Fit a linear model on training samples. Compute the parameter w using Maximum posterior (least square regression with L2 regularization).
    Inputs:
        * X: the feature matrix of the training samples, a numpy matrix of shape n by p.
        * y: the sample labels, a numpy vector of length n.
        * alpha: the weight of the L2 regularization term in ridge regression, a float scalar.
    Outputs:
        * w: the weights of the linear regression model, a numpy float vector of length p.
    Hints:
        * You could use np.eye() to generate an identity matrix.
        * This problem can be solved using 1 line(s) of code.
'''
#---------------------
def ridge_regression(X, y, alpha=0.001):
    #########################################
    ## INSERT YOUR CODE HERE
    w = np.linalg.inv(X.T@X + alpha * (np.eye(X.shape[1])))@X.T@y
    #########################################
    return w
#---------------------
    '''
        TEST: Now you can test the correctness of your code above by typing the following in the terminal:
        ---------------------------------------------------
        nosetests -v test1.py:test_ridge_regression
        ---------------------------------------------------
    '''

#--------------------------------------------

'''
    TEST problem 1:
        Now you can test the correctness of all the above functions by typing the following in the terminal:
        ---------------------------------------------------
        nosetests -v test1.py
        ---------------------------------------------------

        If your code passed all the tests, you will see the following message in the terminal:
        ----------- Problem 1 (10 points in total)--------------------- ... ok
        * (5 points) least square ... ok
        * (5 points) ridge regression ... ok
        ----------------------------------------------------------------------
        Ran 2 tests in 0.004s

        OK
'''

#--------------------------------------------

#--------------------------------------------
'''
    List of All Variables

* p:  the number of dimensions in the feature space.
* X:  the feature matrix of the training samples, a numpy matrix of shape n by p.
* y:  the sample labels, a numpy vector of length n.
* w:  the weights of the linear regression model, a numpy float vector of length p.
* alpha:  the weight of the L2 regularization term in ridge regression, a float scalar.

'''
#--------------------------------------------


# -----------------------------------------------------------
#  Códigos para a primeira lista de exercícios
#  Processamento Adapatativo de Sinais
#  2019.2
#  COPPE/UFRJ
# -----------------------------------------------------------

import numpy as np
import sympy as sp

from utils.arminha import arma
from utils.eigen import eigenstuff, symbolic_diagonalize
from utils.mse_plot import mse_plt
from utils.newton_method import newton
from utils.steepest_descent import sd
from utils.wiener import wiener_solution

def questao_3():
    # -----------------------------------------------------------
    # Exercício 3
    # -----------------------------------------------------------

    # Letra (a)
    print('\n \n 3 - Letra (a) \n')
    R = (1 / 4) * np.matrix([[4, 3, 2, 1], [3, 4, 3, 2], [2, 3, 4, 3], [1, 2, 3, 4]])
    eigenstuff(R)

    # Letra (b)
    print('\n 3 - Letra (b) \n')
    R = np.matrix([[1, 0.95, 0.9025, 0.857374], [0.95, 1, 0.95, 0.9025], [0.9025, 0.95, 1, 0.95],
                   [0.857375, 0.9025, 0.94, 1]])
    eigenstuff(R)

    # Letra (c)
    print('\n 3 - Letra (c) \n')

    sigma_squared = 1
    R = 50 * sigma_squared * np.matrix([[1, 0.9899, 0.98, 0.970], [0.9899, 1, 0.9899, 0.98],
                                        [0.98, 0.9899, 1, 0.9899], [0.970, 0.98, 0.9899, 1]])
    eigenstuff(R)

    # Letra (d)
    print('\n 3 - Letra (d) \n')
    R = np.matrix([[1, 0.5, 0.25, 0.125], [0.5, 1, 0.5, 0.25], [0.25, 0.5, 1, 0.5],
                   [0.125, 0.25, 0.5, 1]])
    eigenstuff(R)

def questao_4():
    # -----------------------------------------------------------
    # Exercício 4
    # -----------------------------------------------------------

    print('\n Exercício 4')

    a_1 = sp.symbols('a_1')
    a_2 = sp.symbols('a_2')

    R = (1 / 4) * sp.Matrix([[a_1, a_2], [a_2, a_1]])
    symbolic_diagonalize(R)

def questao_9():
    # -----------------------------------------------------------
    # Exercício 9
    # -----------------------------------------------------------
    x, _, R = arma(num_pts=1000000, mean=0, sigma=0.1)
    print(np.mean(x), np.var(x))
    print(R)

def questao_15(R, p):
    # -----------------------------------------------------------
    # Exercício 15
    # -----------------------------------------------------------

    # Letra (a)

    print('\n \n  15 - Letra (a) \n')

    w_0 = wiener_solution(R, p)
    print(f'The Wiener solution:')
    print(f'{w_0}')
    return w_0

def questao_16(R, p, num_iteracoes):
    # -----------------------------------------------------------
    # Exercício 16
    # -----------------------------------------------------------
    w_list = sd(R=R, p=p, num_iteracoes=num_iteracoes)
    # for n, w in enumerate(w_list):
    #     print('\n')
    #     print(n, w)

    # print(f'último w do sd: {w_list[-1]}')
    mse_plt(np.array(wiener_solution(R, p)), w_list)

def questao_17(R, p, num_iteracoes):
    # -----------------------------------------------------------
    # Exercício 17
    # -----------------------------------------------------------
    w_list = newton(R=R, p=p, num_iteracoes=num_iteracoes)
    # for n, w in enumerate(w_list):
    #     print('\n')
    #     print(n, w)

    # print(f'último w do newton: {w_list[-1]}')
    mse_plt(np.array(wiener_solution(R, p)), w_list)

if __name__ == "__main__":

    # questao_3()
    # questao_4()

    # questao_9()

    # ----- Para a questão 15, 16 e 17)
    R = (1 / 4) * np.matrix([[4, 3, 2, 1], [3, 4, 3, 2], [2, 3, 4, 3], [1, 2, 3, 4]])
    p = np.matrix([[1 / 2], [3 / 8], [2 / 8], [1 / 8]])
    # questao_15(R, p)
    questao_16(R, p, num_iteracoes=1000)
    #questao_17(R, p, num_iteracoes=1000)


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

def insertionSort(arr):
	n = len(arr)

	for i in range(1, n):

		k = arr[i]
		j = i - 1

		while j >= 0 and arr[j] > k:
			arr[j + 1] = arr[j]
			j -= 1

		arr[j + 1] = k

	return arr

def bubbleSort(arr):
	n = len(arr)

	for i in range(n):
		for j in range(n - i - 1):
			if arr[j] > arr[j + 1]:
				arr[j], arr[j + 1] = arr[j + 1], arr[j]

	return arr

def binarySearch(arr, el):
	arr = bubbleSort(arr)
	l = 0
	r = len(arr) - 1

	while l <= r:

		m = (l + r) // 2

		if el == arr[m]:
			return True
		elif el < arr[m]:
			r = m - 1
		else:
			l = m + 1

	return False

def recurring_delimiter(str_input):
	new_str = ''
	for i in str_input:
		if i not in new_str:
			new_str += i

	return new_str

def fibo(n):
	if n == 0:
		return 0
	elif n == 1:
		return 1
	else:
		return (fibo(n - 1) + fibo(n - 2))

for i in range(10):
	print(fibo(i))

def list_reverse(arr):
	n = len(arr)
	for i in range(n):
		print(arr[n - i - 1])

def palindrome(input_str):

	n = len(input_str)
	o = n / 2 if n % 2 == 0 else n // 2
	i = 0

	while o > 0:
		if input_str[i] == input_str[n - i - 1]:
			i += 1
			o -= 1
		else:
			return False

	return True

def isPalindrome(input_str):

	p = ''.join(reversed(input_str))

	if input_str == p:
		return True

	return False

def setDuplicates(arr):
	return set([x for x in arr if arr.count(x) > 1])

def gaussian(x, mu=0, sigma=1):
	el_1 = (1 / ((2 * np.pi) ** 0.5 * sigma))
	el_2 = np.exp(-(x - mu) ** 2 / (2 * sigma))

	return el_1 * el_2

def rolling_mean(arr, step=10):
	def get_mean(elements):

		n = len(elements)
		s = 0
		for i in range(n):
			s += elements[i]

		return s / n

	roll_mean = []
	arr_length = len(arr)

	for i, el in enumerate(range(step, arr_length)):
		roll_mean.append(get_mean(arr[i:step + i]))

	return roll_mean

def split(input_str):
	len_str = len(input_str) - 1
	rev_str = ''

	for i in range(0, len_str + 1):
		rev_str += input_str[len_str - i]

	return rev_str

print(split('nice interview'))

def online_generator(max):
	number = 1
	while number < max:
		number += 1
		yield number

def medianFinder(arr):
	def bubbleSort(arr):

		n = len(arr)
		for i in range(n):
			for j in range(n - i - 1):
				if arr[j] > arr[j + 1]:
					arr[j], arr[j + 1] = arr[j + 1], arr[j]

		return arr

	arr = bubbleSort(arr)
	n = len(arr)
	m = (n - 1) // 2

	return arr[m]

arr = [3, 7, 6, 5, 9, 8, 1]
print('Unsorted array is ', arr)
print('Sorted array is ', insertionSort(arr))
print(binarySearch(arr, 7))

#                1
#              /   \
#             2     3
#            / \   /
#           4   5 6

# Binary Tree...
# Binary Tree...
class BinaryTree:

	def __init__(self, data):

		self.data = data
		self.right = None
		self.left = None

	def insert(self, data):

		if self.data:
			if data < self.data:
				if self.left is None:
					self.left = BinaryTree(data)
				else:
					self.left.insert(data)
			elif data > self.data:
				if self.right is None:
					self.right = BinaryTree(data)
				else:
					self.right.insert(data)
		else:
			self.data = data

	def PrintTree(self):
		if self.left:
			self.left.PrintTree()
		print(self.data)
		if self.right:
			self.right.PrintTree()

	# Left -> Root -> Right
	def InOrderTraversal(self, root):
		res = []
		if root:
			res = self.InOrderTraversal(root.left)
			res.append(root.data)
			res = res + self.InOrderTraversal(root.right)
		return res

	# Root -> Left -> Right
	def PreOrderTraversal(self, root):
		res = []
		if root:
			res.append( root.data )
			res = res + self.PreOrderTraversal(root.left)
			res = res + self.PreOrderTraversal(root.right)
		return res

	# Postorder traversal
	# Left -> Right -> Root
	def PostOrderTraversal(self, root):
		res = []
		if root:
			res = self.PostOrderTraversal(root.left)
			res = res + self.PostOrderTraversal(root.right)
			res.append(root.data)
		return res

	def InvertTree(self, root):
		self.solve(root)
		return root

	def solve(self, root):
		if not root:
			return
		root.left, root.right = root.right, root.left
		self.solve(root.left)
		self.solve(root.right)

bt = BinaryTree(10)
bt.insert(5)
bt.insert(7)
bt.insert(11)
bt.insert(15)
bt.PrintTree()

print(bt.InOrderTraversal(bt))
print(bt.PreOrderTraversal(bt))
print(bt.PostOrderTraversal(bt))

new_bt = bt.InvertTree(bt)
new_bt.PrintTree()

def solution(A):
    # write your code in Python 3.6
    _ = len( list(set(sorted( A ))) )
    f = float('inf')

    for i in range(_):
        if i + 1 in A:
            pass
        else:
            if i < f:
                f = i + 1

    if f == float('inf'):
        return _ + 1
    else:
        return f


from sympy import oo
from sympy.core.symbol import Symbol
from sympy.functions.elementary.miscellaneous import Min, Max

def test_Min():
    n = Symbol('n', negative=True)
    n_ = Symbol('n_', negative=True)
    nn = Symbol('nn', nonnegative=True)
    nn_ = Symbol('nn_', nonnegative=True)
    p = Symbol('p', positive=True)
    p_ = Symbol('p_', positive=True)
    np = Symbol('np', nonpositive=True)
    np_ = Symbol('np_', nonpositive=True)
    assert Min(5, 4) == 4

    assert Min(-oo, -oo) == -oo
    assert Min(-oo, n) == -oo
    assert Min(n, -oo) == -oo
    assert Min(-oo, np) == -oo
    assert Min(np, -oo) == -oo
    assert Min(-oo, 0) == -oo
    assert Min(0, -oo) == -oo
    assert Min(-oo, nn) == -oo
    assert Min(nn, -oo) == -oo
    assert Min(-oo, p) == -oo
    assert Min(p, -oo) == -oo
    assert Min(-oo, oo) == -oo
    assert Min(oo, -oo) == -oo
    assert Min(n, n) == n
    assert Min(n, np) == Min(n, np)
    assert Min(np, n) == Min(np, n)
    assert Min(n, 0) == n
    assert Min(0, n) == n
    assert Min(n, nn) == n
    assert Min(nn, n) == n
    assert Min(n, p) == n
    assert Min(p, n) == n
    assert Min(n, oo) == n
    assert Min(oo, n) == n
    assert Min(np, np) == np
    assert Min(np, 0) == np
    assert Min(0, np) == np
    assert Min(np, nn) == np
    assert Min(nn, np) == np
    assert Min(np, p) == np
    assert Min(p, np) == np
    assert Min(np, oo) == np
    assert Min(oo, np) == np
    assert Min(0, 0) == 0
    assert Min(0, nn) == 0
    assert Min(nn, 0) == 0
    assert Min(0, p) == 0
    assert Min(p, 0) == 0
    assert Min(0, oo) == 0
    assert Min(oo, 0) == 0
    assert Min(nn, nn) == nn
    assert Min(nn, p) == Min(nn, p)
    assert Min(p, nn) == Min(p, nn)
    assert Min(nn, oo) == nn
    assert Min(oo, nn) == nn
    assert Min(p, p) == p
    assert Min(p, oo) == p
    assert Min(oo, p) == p
    assert Min(oo, oo) == oo

    assert Min(n, n_).func is Min
    assert Min(nn, nn_).func is Min
    assert Min(np, np_).func is Min
    assert Min(p, p_).func is Min

def test_Max():
    assert Max(5, 4) == 5


import numpy as np
import random

## k-means clustering implementation (Lloyd's algorithm) c/o the Data Science Lab (http://datasciencelab.wordpress.com/2013/12/12/clustering-with-k-means-in-python/)

def cluster_points(X, mu):
    clusters  = {}
    for x in X:
        bestmukey = min([(i[0], np.linalg.norm(x-mu[i[0]])) \
                    for i in enumerate(mu)], key=lambda t:t[1])[0]
        try:
            clusters[bestmukey].append(x)
        except KeyError:
            clusters[bestmukey] = [x]
    return clusters

def reevaluate_centers(mu, clusters):
    newmu = []
    keys = sorted(clusters.keys())
    for k in keys:
        newmu.append(np.mean(clusters[k], axis = 0))
    return newmu

def has_converged(mu, oldmu):
    return (set([tuple(a) for a in mu]) == set([tuple(a) for a in oldmu]))

def cluster(X, K):
    # Initialize to K random centers
    oldmu = random.sample(X, K)
    mu = random.sample(X, K)
    clusters = cluster_points(X, mu)
    while not has_converged(mu, oldmu):
        oldmu = mu
        # Assign all points in X to clusters
        clusters = cluster_points(X, mu)
        # Reevaluate centers
        mu = reevaluate_centers(oldmu, clusters)
    return(mu, clusters)

# Given a tuple x and a list of cluster centroids, return the index of the centroid whose cluster is closest
# (i.e. has the least distance) from the tuple
def find_closest_cluster(desc, centroids):
    closest_cluster = 0
    least_distance = None
    for cluster in range(len(centroids)):
        c = centroids[cluster]
        distance = np.linalg.norm(desc-c)
        if least_distance is None or distance < least_distance:
            least_distance = distance
            closest_cluster = cluster

    return closest_cluster


"""
The purpose of this code is to find out the general form of ellipse equation
by performing matrix operations. Then proceed to find perihelion, aphelion,
period, eccentricity, semi major and minor axes.
"""

import numpy as np
import matplotlib.pyplot as plt

def semiaxis(a,b,c,d,f,g):
    """
    This function takes in the coefficients of the expanded ellispe function,
    and calculate both the semi-major and semi-minor axes in AU.
    These equations are taken from: http://mathworld.wolfram.com/Ellipse.html
    """
    numerator = 2*((a*(f**2))+(c*(d**2))+(g*(b**2))-(2*b*d*f)-(a*c*g))
    aDenominator = ((b**2)-a*c)*(np.sqrt((a-c)**2 + 4*(b**2)) - (a+c))
    bDenominator = ((b**2)-a*c)*(-np.sqrt((a-c)**2 + 4*(b**2)) - (a+c))
    major = np.sqrt(numerator/aDenominator)
    minor = np.sqrt(numerator/bDenominator)
    return major,minor

def period(a,b,l1,v1):
    """
    This function takes semi-major, semi-minor axes, perihelion, and aphelion
    distance to calculate the period of the orbit in years.
    This equation is taken from Lab4 handout.
    """
    return (2*np.pi*a*b)/(l1*v1)

def eccentricity(a, b):
    """
    This function takes semi-major and semi-minor axes to calculate
    the eccentricity of the orbit.
    These equations are taken from: http://mathworld.wolfram.com/Ellipse.html
    """
    return np.sqrt(1-((b**2)/(a**2)))

def distances(a, b):
    """
    This function takes semi-major and semi-minor axes to calculate the
    perihelion and aphelion distances in AU.
    This equation is originally taken from Lab4 handout, since we know a and b,
    we can solve the system of equations to find l1 and l2.
    """
    l1 = a - np.sqrt(a**2 - b**2)
    l2 = a + np.sqrt(a**2 - b**2)
    return l1,l2

def maxVelocity(l1, l2, v2):
    """
    This function takes perihelion, aphelion distances and minimum velocity
    to calculate the maximum velocity at the aphelion in AU/year.
    This equation is taken from Lab4 handout.
    """
    return (l2*v2)/l1

# X and Y given in table 1 in AU
x = [-38.04, -35.28, -25.58, -28.80, -30.06]
y = [27.71, 17.27, 30.68, 31.50, 10.53]

#Calculating parameters for our ellipse equation
#Matrix for X parameters
XMatrix = np.asarray([[x[i]**2, x[i]*y[i], y[i]**2, x[i], y[i], 1] for i in range(len(x))])
#Matrix for Y
YMatrix = np.zeros((6,6))
YMatrix[2][0] = 2
YMatrix[0][2] = 2
YMatrix[1][1] = -1/4
#find the coefficients by calculating the eigenvalues of the following
S_inv = np.linalg.inv(np.dot(XMatrix.T, XMatrix))
E, V = np.linalg.eig(np.dot(S_inv,YMatrix))
maxarg = np.argmax(np.abs(E))
#our ellipse coefficients are inside this list.
#Note that B, D, F are all multiplied by 2
A,B,C,D,F,G = V[:, maxarg]

#calculating answers to the question
minSpeed = 1350.* (31536000/149598073000) #m/s -> AU/year
semiMajorAxis, semiMinorAxis = semiaxis(A,B/2,C,D/2,F/2,G) #AU
L1, L2 = distances(semiMajorAxis, semiMinorAxis) #AU
maxSpeed = maxVelocity(L1, L2, minSpeed) #AU/year
e = eccentricity(semiMajorAxis, semiMinorAxis)
T = period(semiMajorAxis, semiMinorAxis, L1, maxSpeed) #year
print("Perihelion distance=", L1, "AU")
print("Aphelion distance=", L2, "AU")
print("Period =", T, "years")
print("Eccentricity =", e)
print("Semi-major axis =", semiMajorAxis, "AU")
print("Semi-minor axis =", semiMinorAxis, "AU")

#setting up for the contour graph
#setting the range for our x and y coordinates
xlinspace = np.linspace(-40,5, 400)
ylinspace = np.linspace(-5,35, 400)
xmesh, ymesh = np.meshgrid(xlinspace, ylinspace)
#using our coefficients from above calculations
#we can use the below equations to fit our data points into the orbit
#the contour function will create a range of z values, however we only want
#z = 0. Thus we will display only the first level which is when z=0
z = A*(xmesh**2) + B*xmesh*ymesh +C*(ymesh**2) + D*xmesh + F*ymesh + G

#creating plots
fig1, graph = plt.subplots()
graph.plot(x, y, "x", label="given data points")
graph.plot(0, 0, "o", label="Sun")
graph.contour(xmesh, ymesh, (z), [0])
graph.set(xlabel="x coordinate (AU)", ylabel="y coordinate (AU)",
       title="Orbit of our comet")
graph.grid()
graph.legend()
fig1.savefig("q1.png")
plt.show()


# Q13

import numpy as np
import matplotlib.pyplot as plt

def f_x(a):
    def f(t):
        return a * np.cos(t) ** 3

    return f

def f_dx(a):
    def f(t):
        return a * -3 * np.sin(t) * (np.cos(t) ** 2)

    return f

def f_y(a):
    def f(t):
        return a * np.sin(t) ** 3

    return f

def f_dy(a):
    def f(t):
        return a * 3 * (np.sin(t) ** 2) * (np.cos(t))

    return f

def f_integrand(a):
    y_dy = f_dy(a);
    x_dx = f_dx(a);

    def f(t):
        return np.sqrt(x_dx(t) ** 2 + y_dy(t) ** 2)

    return f

def mc_integrate(f, a, b, n=1000000):
    # Monte Carlo
    vals = np.random.uniform(a, b, n)
    y = np.vectorize(f)(vals)

    y_mean = np.sum(y) / n
    integ = (b - a) * y_mean

    return integ

x = np.linspace(0, np.pi * 2)
y = np.vectorize(f_integrand(1))(x)

plt.plot(x, y)
plt.show()

mc_area = mc_integrate(f_integrand(1), 0, np.pi * 2)
print(f"MC Area: {mc_area}")

# 2 * np.sqrt(3) => 3.4641016151377544
# 3 * np.sqrt(3) => 5.196152422706632
# area is 6.0030577246980386


import numpy as np

def fft(w: complex, s: np.ndarray) -> np.ndarray:
    """高速フーリエ変換(Fast Fourier Transform, FFT)

    w はオイラーの公式から求められる複素数 exp(-i2π/n)
    F(t) = w^t としたとき、F(0) = 1, F(n) = exp(-i2π/n)^n = exp(-i2π) = 1 となる。
    つまり周期はnである。関数Fは、イメージとしては複素平面上で時計回りに単位円を描く複素数を出力する周期nの関数である。

    FFTは以下の条件を求める。
    (A) w^n = 1
    (B) n が2のべき乗である

    Args:
        w (complex): 複素数 exp(-i2π/n)
        s (np.ndarray[shape=(2**p,)]): データ

    Returns:
        np.ndarray[shape=(2**p,), dtype=complex]: フーリエ変換後の値
    """

    n = len(s)
    if n == 1: return s

    # 高速化が無駄になるため、チェックはコメントアウト
    # 実部を有効桁数10桁で丸めて1と比較。
    # if np.round(np.real(w ** n), 10) == 1 or n & (n - 1) == 0:
    #     raise FFTError("FFT Invalid Condition")

    f0 = fft(w*w, s[::2])
    f1 = fft(w*w, s[1::2])

    return np.concatenate([[f0[j] + w**j*f1[j] for j in range(n//2)],
                           [f0[j] + w**(n/2+j)*f1[j] for j in range(n//2)]])

class FFTError(Exception):
    """FFTの条件についてのエラー"""

    pass


import numpy as np
from scipy.integrate import odeint
from matplotlib import pyplot as plt

v0 = 100 #m/s Initial speed
th0 =80 * (np.pi / 180) #deg Initial flight path angle
h0 = 0 #m launching altitude
s0 = 0 #m launching position

Mass = 0.5 #kg Projectile Mass
g = 9.81 # m/s^2 Gravity Acceleration
Cd = 0.01 # Drag Coefficient
S = 0.5 # m^2 Frontal Area
rho = 1.225 # Air Density kg/m^3

def idealBallistic(V, Th, M, h0 = 0):
    vy0 = V * np.sin(Th)
    vx0 = V * np.cos(Th)

    tFlight = vy0 / g + np.sqrt( (2 * h0 + ( vy0 ** 2 / g) ) / g )
    hMax = h0 + (vy0 ** 2) / (2 * g)
    sMax = vx0 * ( vy0 / g + np.sqrt(( 2 * h0 + ( vy0 ** 2 / g) ) / g ))

    return (tFlight, hMax, sMax)

def plotIdealBallistic(V, Th, M, h0=0):
    T, H, S = idealBallistic(V, Th, M)

    Ndiscr = T / 0.5
    t = np.linspace(0,T,Ndiscr)
    x = V * np.cos(Th) * t
    y = h0 + V * np.sin(Th) * t - 0.5 * g * t ** 2

    plt.plot(x,y,'r')

def ddtV(V, Th):
    return (- Drag(rho, V) / Mass ) -  (g * np.sin(Th))

def ddtTh(V, Th):
    return -g * np.cos(Th) / V

def Drag(rho, V):
    return 0.5 * rho * S * V**2 * S * Cd

def ddtSys(Z, t):
    V, Th = Z
    return [ddtV(V, Th), ddtTh(V, Th)]

t = np.linspace(0, 50, 10000)

Z0 = [v0, th0]
Z = odeint(ddtSys, Z0, t)
v, th = Z.T

vx = v * np.cos(th)
vy = v * np.sin(th)

h = np.zeros(len(t))
h[0] = h0
for i in range(0,len(t)-1):
    h[i+1] = h[i] + vy[i] * (t[i+1] - t[i])

h = np.array(list(filter(lambda x : x >= 0, h)))
t = t[0:len(h)]

s = np.zeros(len(t))
s[0] = s0
for i in range(0,len(t)-1):
    s[i+1] = s[i] + vx[i] * (t[i+1] - t[i])

# vy = list(filter(lambda x : x >= 0, vy))
# vx = vx[0:len(vy)]
# t = t[0:len(vy)]

outText = """***TRAJECTORY RESULTS***

Input Data:

Initial Velocity            {v0:<5} m/s
Initial Flight Path Angle   {th0:<5.2f} deg
Projectile Mass             {m0:<5} kg
Drag Coefficient            {cd:<5}
Reference Front Area        {S:<5} m^2

Output Data:

Maximum Altitude        {hmax:>7.3f} m
Travelled Distance      {sfin:>7.3f} m
Time of Flight          {tfin:>7.3f} s
""".format(
    v0=v0,
    th0=(th0*(180 / np.pi)),
    m0=Mass,
    cd=Cd,
    S=S,
    hmax=h.max(0),
    sfin=s[-1],
    tfin=t[-1]
)

print(outText)

_, hmax, smax = idealBallistic(v0, th0, Mass)

scaleL = hmax if hmax > smax else smax

fig = plt.figure(1)
ax = fig.add_subplot(1,1,1)

'''
ax.set_xticks(np.arange(0,1000,100))
ax.set_yticks(np.arange(0,1000,100))
'''
ax.grid(b=True)
ax.set_ylim([0, scaleL*1.05])
ax.set_xlim([0, scaleL*1.05])

plotIdealBallistic(v0, th0, Mass)
plt.plot(s, h)
plt.title('Trajectory\nTime of Flight {:.3f} s'.format(t[-1]))
plt.xlabel('Distance [m]'), plt.ylabel('Altitude [m]')
plt.legend(['Ideal','Real'])

plt.show()


# -*- coding: utf-8 -*-
import numpy as np
from scipy.optimize import leastsq
import pylab as pl

def func(x, p):
    """
    数据拟合所用的函数: A*sin(2*pi*k*x + theta)
    """
    A, k, theta = p
    return A * np.sin(2 * np.pi * k * x + theta)

def residuals(p, y, x):
    """
    实验数据x, y和拟合函数之间的差，p为拟合需要找到的系数
    """
    return y - func(x, p)

x = np.linspace(0, -2 * np.pi, 100)
A, k, theta = 10, 0.34, np.pi / 6  # 真实数据的函数参数
y0 = func(x, [A, k, theta])  # 真实数据
y1 = y0 + 2 * np.random.randn(len(x))  # 加入噪声之后的实验数据

p0 = [7, 0.2, 0]  # 第一次猜测的函数拟合参数

# 调用leastsq进行数据拟合
# residuals为计算误差的函数
# p0为拟合参数的初始值
# args为需要拟合的实验数据
plsq = leastsq(residuals, p0, args=(y1, x))

print u"真实参数:", [A, k, theta]
print u"拟合参数", plsq[0]  # 实验数据拟合后的参数

pl.plot(x, y0, label=u"真实数据")
pl.plot(x, y1, label=u"带噪声的实验数据")
pl.plot(x, func(x, plsq[0]), label=u"拟合数据")
pl.legend()
pl.show()


import numpy as np

def cosine_distance(a,b):
  """
  the cosine similarity of two vectors.
  """

  cost = np.dot(a,b)/(np.linalg.norm(a)*np.linalg.norm(b))

  return cost


# coding: utf-8

# In[3]:

import numpy as np
from matplotlib import pyplot as plt

# In[4]:

# Set up basic step sizes
N = 50
deltat = 1.0/N # increment from 0 to 1

# In[5]:

# the DFT function of p
def ftilde(p):
    sum = 0.0j
    for n in range(N):
        sum += f(n*deltat)*np.exp(1j*2.0*np.pi*p*n/N)
    return sum

# In[6]:

# the inverse DFT
def finverse(n):
    sum = 0.0j
    for p in range(0, N):
        sum += ftilde(p)*np.exp(-1j*2.0*np.pi*p*n/N)
    return sum/N

# In[7]:

# "Simple" function
def f(x):
    #val = -100*x**2+100*x**4
    val = -100*x**2+100*x**4+10*np.sin(x*100) # high-freq complication
    if(x>0.4 and x<0.6):
        return val*10*abs(x-0.5) # minor blip in the middle
        #return val
    return val

# In[8]:

# plot f(t) with N points
tarray = np.zeros(N)
farray = np.zeros(N)
for i in range(0, N):
    tarray[i] = i*deltat
    farray[i] = f(i*deltat)
plt.clf()
plt.plot(tarray,farray,'.-',label="Discrete f(t) with "+str(N)+" points",alpha=0.2)
plt.legend()
plt.show()

# In[9]:

# plot ftilde (both Real and Imaginary parts)
parray = np.arange(0,N)
ftildearrayR = np.zeros(parray.size)
for p in range(0, parray.size):
    ftildearrayR[p] = np.real(ftilde(p))
ftildearrayI = np.zeros(parray.size)
for p in range(0, parray.size):
    ftildearrayI[p] = np.imag(ftilde(p))

# In[10]:

plt.clf()
plt.plot(parray, ftildearrayR,'.-',label="ftilde Real",
         alpha=0.2)
plt.legend()
plt.show()

# In[11]:

plt.plot(parray, ftildearrayI,'.--',label="ftilde Imag",
         alpha=0.6)
plt.legend()
plt.show()

# In[12]:

tarray = np.zeros(N)
farrayR = np.zeros(N)
farrayI = np.zeros(N)
for i in range(0, N):
    tarray[i] = i*deltat
    farrayR[i] = np.real(finverse(i))
    farrayI[i] = np.imag(finverse(i))

# In[13]:

plt.clf()
plt.plot(tarray,farrayR,'.-',label="fInvReal",alpha=0.4)
plt.legend()
plt.show()

# In[14]:

plt.plot(tarray,farrayI,'.-',label="fInvImag",alpha=0.4)
plt.legend()
plt.show()

# In[ ]:

# In[ ]:


import numpy as np

def get_null_space(A):
    """
        Computes the Null space (all the span of vectors v for which dot(A, v) = 0 holds)
    :param A: Matrix from which to compute the null space
    :return: A matrix q, whose columns make up the null space of A
    """
    m, n = A.shape
    # TODO: Convert assertion into proper exception or error message here
    assert n > m
    q, r = np.linalg.qr(A, mode='complete')
    null_space = q[:, n-m:]
    return null_space

def get_orthogonal_vector(A, mode='qr'):
    """
    :param A: Vectors arranged as columns of a matrix A, relative to which an orthogonal vector is found.
    :param mode: 'qr' uses QR decomposition for the calculation (no other method is currently supported)
    :return: A vector, which is orthogonal to the vectors, which make up the columns of A
    """
    if mode == 'qr':
        null_space = get_null_space(A)
        return null_space[:, -1]
    else:
        print('Unsupported mode')
        raise AssertionError


# Project Euler Problem 2 Solution
#
# Problem statement:
# The prime factors of 13195 are 5, 7, 13 and 29. What
# is the largest prime factor of the number 600851475143?
#
# Solution description:
# Brute-force.
#
# Author: Daniel Schuette
# Date: 2018/09/30
# License: MIT (see ../LICENSE.md)
import math
import time

import numpy as np

def is_prime(n):
    """
    This function returns a boolean value indicating whether
    the input `n' is a primer number or not.
    """
    # handle edge cases
    if n == 1:
        return False
    elif n == 2:
        return True
    elif n % 2 == 0:
        return False

    for i in range(int(math.sqrt(n)+1)):
        if (i == 0) or (i == 1):
            continue
        if (n % i) == 0:
            return False
    return True

def largest_prime_factor(n, div=2):
    """
    This function returns the largest prime factor of
    input integer `n'.
    """
    target = n
    i = n // div  # the largest prime factor must be smaller than n//div
    while True:
        i -= 1
        if i <= 1:
            break  # pragma: no cover
        if (target % i) == 0:
            if is_prime(i):
                return i

if __name__ == "__main__":
    start = time.time()
    target = 600851475143
    solution = largest_prime_factor(target, div=2900)
    end = time.time()

    # `largest_prime_factor' implements the brute-force solution
    print("expired: {}s, solution: {}".format(
        np.round(end - start), solution))


"""
NEWTON-PAPHSON
Blanca Cano Camarero

"""

import numpy as np
import sympy as sp

x = sp.symbols('x')

def newtonRaphson(f, inicio, tolerancia =10*(-5), precision=10*(-5), max_iter=20, df=None):

    if df == None:
        df = sp.lambdify(x, sp.diff(f,x), "numpy")
    f = sp.lambdify(x, f)
    c = inicio
    cnt = 1
    no_encontrado = True
    salida = "max_iter"
    while cnt < max_iter and no_encontrado :

        cs = c - f(c)/df(c)
        #cs = f(c)

        if abs(f(cs)) < precision:
            salida = "precision"
            no_encontrado = False
        if abs(cs-c) < tolerancia:
            salida = "tolerancia"
            no_encontrado = False
        else:
            c = cs
            cnt +=1

    return cs , salida, cnt

def newtonRaphsonSinInicio(f,a,b, tolerancia=10**(-5), precision=10**(-10), max_iter=100):
    # Calculamos derivadas

    ddf = sp.lambdify(x,sp.diff(f,x,2),"numpy")

    #calculamos punto inicial
    if f.evalf(subs={x:a})*ddf(a) > 0 :
        c = a
    else :
        c = b

    return newtonRaphson(f,inicio=c , tolerancia = tolerancia, precision = precision, max_iter = max_iter)

def newtonRaphsonAcelerado (f, inicio, tolerancia =10*(-5), precision=10*(-5), maxIter=30, df = None):

    if df == None:
        df = sp.simplify(sp.diff(f,x))

    return newtonRaphson((f/df), inicio, tolerancia,precision)

def horner(x,coef, d = 0):
    """Algoritmo de horner para evaluación rápida de polinomio
x es el punto en el que evaluar el polinomio
coef es una lista con los coeficientes a evaluar de $p = \sum_{i=0}^n \alpha_i x^i$

d es la derivada del polinomio a calcular
    """

    n = len(coef)

    salida = 0
    for i in range(n,d,-1):
        salida = sp.factorial(i-1)/sp.factorial(i-d-1)*coef[i-1] + x*salida

    return salida

## Aprovechamos nuestras implementaciones ya hechas
def newtonRapshonHorner(coef,semilla, tolerancia =10*(-5), precision=10*(-5), max_iter=20):
    """ Algoritmo de newtonRapshon para evaluar polinomio de manera rápida"""
    f = horner(x,coef,0)
    midf = sp.lambdify(x, horner(x,coef,1))
    return newtonRaphson(f, 10, tolerancia =10*(-5), precision=10*(-5), max_iter=20,df=midf)


import numpy as np
from problem_3a import inverse_iteration
from problem_3b import rayleigh_iter

def main():
	print("Inverse Iteration")
	print("-------------------\n\n")

	seeds = [1, 89, 98, 23, 88, 91, 101, 11, 17, 19]

	for i in range(10):
		A = np.array([[6, 2, 1], [2, 3, 1], [1, 1, 1]])
		np.random.seed(seeds[i])
		print('Seed:', seeds[i])
		x0 = np.random.random((3,))
		l, x, iters = inverse_iteration(A, x0)
		vals, vecs = np.linalg.eig(A)
		rel_err_vals = np.min(np.abs(vals - l) / vals[np.argmin(np.abs(vals - l))])
		print("Computed Eigenvalue:", l)
		print("Computed Eigenvector:\n\t", x, "'\n")
		rel_err_vecs = np.linalg.norm(x - vecs[:, np.argmin(np.abs(vals - l))], ord=2) / np.linalg.norm(vecs[:, np.argmin(np.abs(vals - l))])
		print("Relative Error in Eigenvalues:", rel_err_vals)
		print("Relative Error in Eigenvectors:", rel_err_vecs)
		print("Number of iterations:", iters)
		print("\n\n")

	print("Rayleigh Quotient Iteration")
	print("------------------------------\n\n")

	seeds = [1, 89, 98, 23, 88, 91, 101, 11, 17, 19]

	for i in range(10):
		A = np.array([[6, 2, 1], [2, 3, 1], [1, 1, 1]])
		np.random.seed(seeds[i])
		print('Seed:', seeds[i])
		x0 = np.random.randn(3)
		l, x, iters = rayleigh_iter(A, x0)
		vals, vecs = np.linalg.eig(A)
		rel_err_vals = np.min(np.abs(vals - l) / vals[np.argmin(np.abs(vals - l))])

		rel_err_vecs = np.linalg.norm(x - vecs[:, np.argmin(np.abs(vals - l))], ord=2) / np.linalg.norm(vecs[:, np.argmin(np.abs(vals - l))])
		print("Computed Eigenvalue:", l)
		print("Computed Eigenvector:\n\t", x, "'\n")
		print("Relative Error in Eigenvalues:", rel_err_vals)
		print("Relative Error in Eigenvectors:", rel_err_vecs)
		print("Number of iterations:", iters)
		print("\n\n")

if __name__ == '__main__':
	main()


import numpy as np

from coptim.optimizer import Optimizer

class NewtonMethod(Optimizer):

    def __init__(self):
        # TODO: More metrics: vector of x's, objective values, etc.
        self.iterations = 0

    def newton_solvable(self, x, gradient, rho, p, hessian):
        inverse = np.linalg.inv(hessian(x))
        d = inverse.dot(-gradient)

        if np.linalg.cond(inverse) > 1e-12:
            if gradient.T.dot(d) > -rho * np.power(np.linalg.norm(d), p):
                return False
        else:
            return False

        return True

    def optimize(self, x_0, rho, p, k_max, beta, sigma, epsilon, func):
        x = x_0
        while self.stopping_criteria(x, func, epsilon, k_max):
            descent_direction = -1 * func.gradient(x)

            # update step
            if self.newton_solvable(x, descent_direction, rho, p, func.hessian):
                inverse = np.linalg.inv(func.hessian(x))
                d = inverse.dot(descent_direction)
            else:
                d = descent_direction

            step_size = self.step_size(beta, sigma, x, d, func)
            x = x + step_size * d

            self.iterations += 1

        return x

    def stopping_criteria(self, x, func, epsilon, k_max):
        return np.linalg.norm(func.gradient(x)) >= epsilon or self.iterations > k_max

    def step_size(self, beta, sigma, x, d, func):
        """
        Armijo's Rule
        """
        i = 0
        inequality_satisfied = True
        while inequality_satisfied:
            if func.eval(x + np.power(beta, i) * d) <= func.eval(x) + np.power(beta, i) * sigma * func.gradient(x).dot(
                    d):
                break

            i += 1

        return np.power(beta, i)


# -*- coding: utf-8 -*-
"""02_IT.ipynb

Automatically generated by Colaboratory.

Original file is located at
    https://colab.research.google.com/drive/1lKoR3GtYObIvUomdAIVgzpvhBsOt1W3T
"""

import numpy as np
import matplotlib.pyplot as plt

x = np.arange(100)
y = 1 / np.log2(x)

print(x)
plt.plot(x, y)

x = np.linspace(0, 1, num=51)
y = 1 / np.log2(x)

print(x)
plt.plot(x, y)

def SIC(x):
  return -np.log2(x)

def ENTROPY(x):
  #y = SIC(x)
  return sum([n1 * n2 for n1, n2 in zip(x, SIC(x))])

x = np.array([0.5, 0.5])
SIC(x), ENTROPY(x)

# 12 die (dices); unbalanced; 1 weighs more than rest
# Q: WWSD?

# c[0] = L > R
# c[1] = L == R
# c[2] = L < R

# 6 v 6
x6 = [0.5, 0.5]
h6 = SIC(x6)
H6 = ENTROPY(x6)
print('6v6', h6, H6)

# 5 v 5
x5 = [5/12, 2/12, 5/12]
h5 = SIC(x5)
H5 = ENTROPY(x5)
print('5v5', h5, H5)

# 4 v 4
x4 = [4/12, 4/12, 4/12]
h4 = SIC(x4)
H4 = ENTROPY(x4)
print('4v4', h4, H4)

# 3 v 3
x3 = [3/12, 6/12, 3/12]
h3 = SIC(x3)
H3 = ENTROPY(x3)
print('4v4', h3, H3)

print("MAX ENTROPY: {}; i.e. weigh {}".format(max(H6, H5, H4, H3), 3))

# ROUND 2; HHL v HLL
R2x3 = [3/8, 2/8, 3/8]
R2h3 = SIC(x3)
R2H3 = ENTROPY(x3)
print('3v3', R2h3, R2H3)


#!/usr/bin/env python

# Plot the softmax function.

import matplotlib.pyplot as plt
import numpy as np
import os
from scipy.misc import logsumexp

def softmax(a):
  e = np.exp((1.0 * np.array(a)))
  return e / np.sum(e)

T = [100, 2, 1]
a = np.array([3, 0, 1]);
ind = [1, 2, 3]

plt.figure(figsize=(12,4))
for i in range(len(T)):
    plt.subplot(1,3,i+1)
    plt.bar(ind, softmax(a / T[i]))
    plt.title('T = %d' % T[i])
    plt.ylim(0, 1)
    ax = plt.gca()
    ax.set_xticklabels([])

plt.show()
fname = 'softmax_temp.pdf'
print(fname)
plt.savefig(os.path.join('../figures', fname))

#for i in range(len(T)):
#    plt.bar(ind, softmax(a / T[i]))
#    plt.ylim(0, 1)
#    plt.title('T = %d' % T[i])
#    plt.show()
#    #plt.savefig('softmax_temp%d.png' % T[i])
#    fname = 'softmax_temp%d.pdf' % T[i]
#    print(fname)
#    plt.savefig(os.path.join('../figures', fname))


#!/usr/bin/env python3
import sys
import numpy as np
import argparse

np.random.seed(2017)

def inside_circle(total_count):

    x = np.float32(np.random.uniform(size=total_count))
    y = np.float32(np.random.uniform(size=total_count))

    radii = np.sqrt(x*x + y*y)

    filtered = np.where(radii<=1.0)
    return len(radii[filtered])

def estimate_pi(total_count):

    count = inside_circle(total_count)
    return (4.0 * count / float(total_count))

if __name__=='__main__':

    parser = argparse.ArgumentParser(description='Estimate Pi using a Monte Carlo method.')
    parser.add_argument('n_samples', metavar='N', type=int, nargs=1,
                        default=10000,
                        help='number of times to draw a random number')

    args = parser.parse_args()

    n_samples = args.n_samples[0]
    my_pi = estimate_pi(n_samples)
    sizeof = np.dtype(np.float32).itemsize

    print("[serial version] required memory %.3f MB" % (n_samples*sizeof*3/(1024*1024)))
    print("[serial version] pi is %f from %i samples" % (my_pi,n_samples))

    sys.exit(0)


from numba import jit
import sys
import numpy as np
import random
import time

@jit
def estimate_nbr_points_in_quarter_circle(nbr_estimates):
    nbr_trials_in_quarter_unit_circle = 0

    for step in range(int(nbr_estimates)):
        x = random.uniform(0,1)
        y = random.uniform(0,1)
        is_in_unit_circle = pow(x,2) + pow(y,2) <= 1.0
        nbr_trials_in_quarter_unit_circle += is_in_unit_circle

    return nbr_trials_in_quarter_unit_circle

@jit
def estimate_nbr_points_in_quarter_circle_numpy(nbr_samples):
    np.random.seed()
    xs = np.random.uniform(0, 1, nbr_samples)
    ys = np.random.uniform(0, 1, nbr_samples)
    estimate_inside_quarter_unit_circle = pow(xs, 2) + pow(ys, 2) <= 1
    nbr_trials_in_quarter_unit_circle = np.sum(estimate_inside_quarter_unit_circle)

    return nbr_trials_in_quarter_unit_circle

if __name__ == '__main__':
    # arg = 100000
    arg = int(sys.argv[1])
    t1 = time.time()
    pi_estimate = estimate_nbr_points_in_quarter_circle(arg) / arg * 4
    t2 = time.time()
    pi_estimate_numpy = estimate_nbr_points_in_quarter_circle_numpy(arg) / arg * 4
    t3 = time.time()

    print("pi_estimate = ",pi_estimate, " time cost : ", t2 - t1)
    print("pi_estimate_numpy = ", pi_estimate_numpy, " time cost : ", t3 - t2)


import numpy as np

def mod1(x, y): return ((x % y) + y) % y

def convolve_cyclic(signal, filter_array):
    output_size = max(len(signal), len(filter_array))
    out = np.zeros(output_size)
    s = 0

    for i in range(output_size):
        for j in range(output_size):
            if(mod1(i - j, output_size) < len(filter_array)):
                s += signal[mod1(j - 1, output_size)] * filter_array[mod1(i - j, output_size)]
        out[i] = s
        s = 0

    return out

def convolve_linear(signal, filter_array, output_size):
    out = np.zeros(output_size)
    s = 0

    for i in range(output_size):
        for j in range(max(0, i - len(filter_array)), i + 1):
            if j < len(signal) and (i - j) < len(filter_array):
                s += signal[j] * filter_array[i - j]
        out[i] = s
        s = 0

    return out

# sawtooth functions for x and y
x = [float(i + 1)/200 for i in range(200)]
y = [float(i + 1)/200 for i in range(200)]

# Normalization is not strictly necessary, but good practice
x /= np.linalg.norm(x)
y /= np.linalg.norm(y)

# full convolution, output will be the size of x + y - 1
full_linear_output = convolve_linear(x, y, len(x) + len(y) - 1)

# simple boundaries
simple_linear_output = convolve_linear(x, y, len(x))

# cyclic convolution
cyclic_output = convolve_cyclic(x, y)

# outputting convolutions to different files for plotting in external code
np.savetxt('full_linear.dat', full_linear_output)
np.savetxt('simple_linear.dat', simple_linear_output)
np.savetxt('cyclic.dat', cyclic_output)


"""
Some Extras
"""
##
## Some extra routines to make the QuadraticForm class more useful.
##

from sage.rings.all import ZZ
from sage.rings.polynomial.polynomial_element import is_Polynomial
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.quadratic_forms.quadratic_form import QuadraticForm

def BezoutianQuadraticForm(f, g):
    r"""
    Compute the Bezoutian of two polynomials defined over a common base ring.  This is defined by

    .. math::

        {\rm Bez}(f, g) := \frac{f(x) g(y) - f(y) g(x)}{y - x}

    and has size defined by the maximum of the degrees of `f` and `g`.

    INPUT:

    - `f`, `g` -- polynomials in `R[x]`, for some ring `R`

    OUTPUT:

    a quadratic form over `R`

    EXAMPLES::

        sage: R = PolynomialRing(ZZ, 'x')
        sage: f = R([1,2,3])
        sage: g = R([2,5])
        sage: Q = BezoutianQuadraticForm(f, g) ; Q
        Quadratic form in 2 variables over Integer Ring with coefficients:
        [ 1 -12 ]
        [ * -15 ]

    AUTHORS:

    - Fernando Rodriguez-Villegas, Jonathan Hanke -- added on 11/9/2008

    """
    ## Check that f and g are polynomials with a common base ring
    if not is_Polynomial(f) or not is_Polynomial(g):
        raise TypeError("Oops!  One of your inputs is not a polynomial. =(")
    if f.base_ring() != g.base_ring():                   ## TO DO:  Change this to allow coercion!
        raise TypeError("Oops!  These polynomials are not defined over the same coefficient ring.")

    ## Initialize the quadratic form
    R = f.base_ring()
    P = PolynomialRing(R, ['x','y'])
    a, b = P.gens()
    n = max(f.degree(), g.degree())
    Q = QuadraticForm(R, n)

    ## Set the coefficients of Bezoutian
    bez_poly = (f(a) * g(b) - f(b) * g(a)) // (b - a)    ## Truncated (exact) division here
    for i in range(n):
        for j in range(i, n):
            if i == j:
                Q[i,j] = bez_poly.coefficient({a:i,b:j})
            else:
                Q[i,j] = bez_poly.coefficient({a:i,b:j}) * 2

    return Q

def HyperbolicPlane_quadratic_form(R, r=1):
    """
    Constructs the direct sum of `r` copies of the quadratic form `xy`
    representing a hyperbolic plane defined over the base ring `R`.

    INPUT:

    - `R`: a ring
    - `n` (integer, default 1) number of copies

    EXAMPLES::

        sage: HyperbolicPlane_quadratic_form(ZZ)
        Quadratic form in 2 variables over Integer Ring with coefficients:
        [ 0 1 ]
        [ * 0 ]

    """
    r = ZZ(r)
    ## Check that the multiplicity is a natural number
    if r < 1:
        raise TypeError("The multiplicity r must be a natural number.")

    H = QuadraticForm(R, 2, [0, 1, 0])
    return sum([H  for i in range(r-1)], H)


# Softmax function : normalized exponential function
# Copyright 2019 Denis Rothman MIT License. See LICENSE.

import math
import numpy as np

# y is the vector of the scores of the lv vector in the warehouse example.
y = [0.0002, 0.2, 0.9,0.0001,0.4,0.6]
print('0.Vector to be normalized',y)

#Version 1 : Explicitly writing the softmax function for this case
y_exp = [math.exp(i) for i in y]
print("1",[i for i in y_exp])
print("2",[round(i, 2) for i in y_exp])
sum_exp_yi = sum(y_exp)
print("3",round(sum_exp_yi, 2))
print("4",[round(i) for i in y_exp])
softmax = [round(i / sum_exp_yi, 3) for i in y_exp]
print("5,",softmax)

#Version 2 : Explicitly but with no comments
y_exp = [math.exp(i) for i in y]
sum_exp_yi = sum(y_exp)
softmax = [round(i / sum_exp_yi, 3) for i in y_exp]
print("6, Normalized vector",softmax)

#Version 3: Using a function in a 2 line code instead of 3 lines
def softmax(x):
    return np.exp(x) / np.sum(np.exp(x), axis=0)

print("7A Normalized vector",softmax(y))
print("7B Sum of the normalize vector",sum(softmax(y)))

ohot=max(softmax(y))
ohotv=softmax(y)

print("7C.Finding the highest value in the normalized y vector : ",ohot)
print("7D.For One-Hot function, the highest value will be rounded to 1 then set all the other values of the vector to 0: ")
for onehot in range(6):
    if(ohotv[onehot]<ohot):
        ohotv[onehot]=0
    if(ohotv[onehot]>=ohot):
        ohotv[onehot]=1
print("This is a vector that is an output of a one-hot function on a softmax vector")
print(ohotv)


"""
A file for executing math functions.
"""

from typing import Union
import numpy as np

def pi(n: Union[int, float] = 1e4) -> float:
    """
    This function computes the value of Pi.

    Parameters
    ----------
    n : Union[int, float], optional
       Number of points to generate.

    Returns
    -------
    pi : float
        The computed value of Pi.

    """
    return 4 * np.mean(np.linalg.norm(np.random.rand(int(n), 2), axis=-1) < 1)

print("pi ", pi(1e6))

def euler(n=10):
    """
    This function computes the value of :math:`e` via a Taylor series expansion.

    .. math::

        e = 1 + \\sum_n^\\infty \\frac{1}{n!}

    Parameters
    ----------
    n : int
        The order of Taylor expansion.

    Returns
    -------
    e_value : float
        The computed value of Euler's number.

    """
    if n < 0:
        raise ValueError("Only positive integers are allowed")

    etaylor = 0.0

    for x in range(0, n + 1):
        etaylor += 1 / fact(x)

    return etaylor

def fact(n):
    fact = 1

    for x in range(1, n + 1):
        fact *= x
    return fact

#print ("The e is : ",end="")
#for x in range(0,10+1):
#    print (x, euler(x))

#print ("The fact is : ",end="")
#for x in range(0,10+1):
#        print (x, fact(x))

#print ("noarg ", euler())


# -*- coding: UTF-8 -*-

import numpy as np

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

# sigmoid函数导数
def sigmoid_derivative(x):
    return sigmoid(x) * (1 - sigmoid(x))

# tanh函数
def tanh(x):
    return (np.exp(x) - np.exp(-x)) / (np.exp(x) + np.exp(-x))

# tanh函数导数
def tanh_derivative(x):
    return 1 - tanh(x) * tanh(x)

# relu函数
def relu(x):
    return np.where(x < 0, 0, x)

# relu函数导数
def relu_derivative(x):
    return np.where(x < 0, 0, 1)

# 规范化
def normalize_rows(x):
    return x / np.linalg.norm(x, axis=1, keepdims=True)

# softmax
def softmax(x):
    exp = np.exp(x)
    return exp / np.sum(exp, axis=1, keepdims=True)


import numpy as np
import matplotlib.pyplot as plt

norm = np.linalg.norm

def a():
    """
    45 degrees
    """
    u = np.array([1, 1])
    v = np.array([0, 1])

    origin = np.array([[0, 0], [0, 0]])  # origin point
    plt.quiver(*origin, u, v, color=['r', 'b'], scale=1)
    plt.show()

    print(np.dot(u, v))

    angle = np.degrees(np.arccos((np.dot(u, v)) / (norm(u) * norm(v))))
    print(angle)

def c():
    """
    168.69 degrees
    """
    u = np.array([-2, 3])
    v = np.array([1 / 2, -1 / 2])
    origin = np.array([[0, 0], [0, 0]])  # origin point
    plt.quiver(*origin, u, v, color=['g', 'y'], scale=1)
    plt.show()

    angle = np.degrees(np.arccos((np.dot(u, v) / (norm(u) * norm(v)))))

    print(angle)

def two_a():
    """
    55.902
    """
    u = np.array([-1, 1, 3])
    v = np.array([3, -1, 5])

    d = np.dot(u, v)
    n = norm(u) * norm(v)
    angle = np.degrees(
        np.arccos(d / n)
    )

    origin = [[0, 0, 0], [0, 0, 0]]

    plt.quiver(*origin, u, v, color=['r', 'g', 'b'], scale=1)
    plt.show()
    print(angle)

def two_b():
    """
    90.0
    """
    u = np.array([1, 0, 0])
    v = np.array([0, 0, 15])

    d = np.dot(u, v)
    n = norm(u) * norm(v)
    angle = np.degrees(np.arccos(d / n))

    origin = [[0, 0, 0], [0, 0, 0]]
    plt.quiver(*origin, u, v, color=['r', 'b', 'g'], scale=1)
    plt.show()
    print(angle)

def two_c():
    """
    64.623
    """
    u = np.array([-1, 2, 3])
    v = np.array([np.sqrt(2), 1 / np.sqrt(2), 1])

    d = np.dot(u,v)
    n = norm(u) * norm(v)

    origin = np.zeros((2,3))

    plt.quiver(*origin, u, v, color=['r', 'b', 'g'], scale=1)
    plt.show()

    angle = np.degrees(np.arccos(d/n))
    print(angle)

two_c()


#!/usr/bin/python3
import sympy

def deriving_finite_difference_approximations(u, x, h, hs, order=1, errors=1):
    '''See also `1_2.py`.
    '''
    # the method of undetermined coefficients
    length = len(hs)
    hs = sympy.Matrix([hs])
    _hs = sympy.ones(1, length)
    A = sympy.ones(length)
    for ith in range(1, length):
        _hs = _hs.multiply_elementwise(hs)
        A[ith, :] = _hs
    b = sympy.zeros(length, 1)
    b[order] = sympy.factorial(order)
    coefficients = A.solve(b)
    # errors
    _errors = [None] * errors
    Du = u(x).diff(x, length-1)
    denominator = sympy.factorial(length-1)
    for ith in range(errors):
        denominator *= length + ith
        _hs = _hs.multiply_elementwise(hs)
        coefficient = coefficients.dot(_hs) / denominator
        Du = Du.diff()
        _errors[ith] = coefficient * h**(length+ith-order) * Du
    # finite difference approximations
    Du = sum(c*u(x+a*h) for c, a in zip(coefficients, hs))
    return Du/h**order, sum(_errors)

if __name__ == '__main__':
    u = sympy.Function('u')
    x, h = sympy.symbols('x, h')

    hs = [0, -1, -2]
    order = 1
    D1u, errors = deriving_finite_difference_approximations(u, x, h, hs, order, 1)
    sympy.pretty_print(D1u + errors)

    hs = [-1, 0, 1]
    order = 2
    D2u, errors = deriving_finite_difference_approximations(u, x, h, hs, order, 2)
    sympy.pretty_print(D2u + errors)

    hs = [-1, 0, 1, 2]
    order = 3
    D3u, errors = deriving_finite_difference_approximations(u, x, h, hs, order, 2)
    sympy.pretty_print(D3u + errors)

    hs = [-2, -1, 1, 2]
    order = 3
    D3u, errors = deriving_finite_difference_approximations(u, x, h, hs, order, 2)
    sympy.pretty_print(D3u + errors)


import numpy as np
import scipy as sp

def norm_cdf_int(mu, std, LB, UB):
    """ Return P(LB < X < UB) for X Normal(mu, std) """
    rv = sp.stats.norm(mu, std)
    return rv.cdf(UB) - rv.cdf(LB)

def norm_cdf_int_approx(mu, std, LB, UB):
    """
    Return P(LB < X < UB) for X Normal(mu, std) using approximation of Normal CDF

    Input: All inputs as 1-D arrays
    """
    l = normal_cdf_approx((LB - mu)/std)
    u = normal_cdf_approx((UB - mu)/std)
    return u - l

def normal_cdf_approx(x):
    """
    Approximation of standard normal CDF

    Input: x = array

    Polynomial approximation from Abramowitz and Stegun p. 932
    http://people.math.sfu.ca/~cbm/aands/frameindex.htm

    Absolute error < 7.5*10^-8
    """
    p = 0.2316419
    b = [0.319381530, -0.356563782, 1.781477937, -1.821255978, 1.330274429]

    xx = abs(x) # Approximation only works for x > 0, return 1 - p otherwise

    t = 1/(1 + p*xx)
    Z = (1/(np.sqrt(2*np.pi)))*np.exp(-(x*x)/2)
    pol = b[0]*t + b[1]*(t**2) + b[2]*(t**3) + b[3]*(t**4) + b[4]*(t**5)

    prob = 1 - Z*pol # For x > 0
    prob[x < 0] = 1 - prob[x < 0] # Change when x < 0

    return prob


import sys
sys.path.append('..')

import autodiff.operations as operations
from autodiff.structures import Number
import numpy as np
from copy import deepcopy

def func(x):
    return 5 * x ** 2 + 10 * x - 8

def newtons_method(func, initial_guess):

    #stores a list of jacobians from each iteration
    jacobians = []

    x0 = initial_guess

    fxn = func(initial_guess)

    fpxn = fxn.jacobian(initial_guess)

    x1 = x0 - fxn/fpxn

    jacobians.append(fpxn)

    while fxn.val > 1e-7:
        x0 = x1

        fxn = func(x0)

        fpxn = fxn.jacobian(x0)

        jacobians.append(fpxn)

        x1 = x0- fxn / fpxn

    return x1, jacobians

if __name__ == '__main__':
    x0 = Number(5)
    xstar, jacobians = newtons_method(func, x0)

    print(xstar, jacobians[-1])


import numpy as np
from matplotlib import pyplot as plt

# Suppose y = k*x + m
def function(k,m,x):
    return k*x+m

def least_square(x,y):
    m = np.array([1 for i in range(len(x))])
    plt.scatter(x, y)
    x = np.array(x)
    A = np.vstack((x, m)).T
    AT = A.T
    ATA = np.dot(AT, A)
    print(ATA)
    y = np.array(y)
    y = np.reshape(y, (-1, 1))
    beta = np.dot(np.dot(np.linalg.inv(ATA), AT), y)
    print(beta)
    print(y)
    output_list = []
    for i in range(len(x)):
        output_list.append(function(beta[0], beta[1], x[i]))
    return output_list

if __name__ == "__main__":
    x = [1, 2, 3, 5, 6, 7, 8, 9]
    y = [2, 4, 4, 6, 7, 8, 9, 10]
    output = least_square(x,y)
    plt.plot(x, output)
    plt.show()


import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm

def g(r):
    return 1/(r * np.sqrt(2 * np.pi)) * np.exp(-r**2/2) + norm.cdf(r)

def f(r):
    return (1 + 1/(r**2)) * norm.cdf(r) +  1/(r * np.sqrt(2 * np.pi)) * np.exp(-r**2/2) - g(r)**2

def E_phi(mu, sigma):
    r = mu/sigma
    return mu * g(r)

def Var_phi(mu, sigma):
    M = mu*norm.cdf(mu/sigma) + sigma*norm.pdf(mu/sigma)
    #r = mu/sigma
    #return mu**2 * f(r)
    return (mu**2 + sigma**2)*norm.cdf(mu/sigma) + mu*sigma*norm.pdf(mu/sigma) - M**2

def linearised_Var_phi(mu, sigma):
    return np.heaviside(mu, 0) * sigma**2

def compare_var_linearised(muU, muv, varU, varv, title='', show=False, xrange=[-4, 4], check_pass_test=False, plot=False):
    # plot true var[phi] versus linearised approx
    plt.figure()
    x = np.linspace(xrange[0], xrange[1], 500)
    mu = muU*x + muv
    sigma = np.sqrt(varU*x**2 + varv)
    # true variance
    Varphi = Var_phi(mu, sigma)
    varphiline, = plt.plot(x, Varphi, label='Var $[\phi(a)]$')
    # linearised variance
    linVarphi = linearised_Var_phi(mu, sigma)
    linvarphiline, =  plt.plot(x, linVarphi, label='truncated quadratic approximation', ls='--')

    if plot == True:
        # plot vertical line showing where the ReLU kink is, for the mean value of the weights
        #kink = -muv/muU
        #plt.axvline(x=kink, ls='--', color='k')
        plt.xlabel('$x$')
        plt.xlim(xrange[0], xrange[1])
        plt.ylabel('variance')
        plt.grid(True)
        plt.legend(handles = [varphiline, linvarphiline])
        plt.title('True and approximate variance when $\mu_U =${}, $\mu_v =${}, $\sigma_U^2 =${}, $\sigma_v^2 =${}'.format(muU, muv, varU, varv))
        if show:
            plt.show()
        else:
            plt.savefig('plots//wing_condition//plot_true_vs_approx_var_' + title + '.pdf')
            plt.close()

    if check_pass_test == True:
        # check if the conditions for David's proof hold
        # import pdb; pdb.set_trace()

        x_S1 = np.linspace(0, 10, 1000)
        mu_S1 = muU*x_S1 + muv
        sigma_S1 = np.sqrt(varU*x_S1**2 + varv)
        x_S2 = np.linspace(-10, 0, 1000)
        mu_S2 = muU*x_S2 + muv
        sigma_S2 = np.sqrt(varU*x_S2**2 + varv)

        Varphi_S1 = Var_phi(mu_S1, sigma_S1)
        Varphi_S2 = Var_phi(mu_S2, sigma_S2)

        x_S2_wing = -x_S1
        mu_S2_wing = muU*x_S2_wing + muv
        sigma_S2_wing = np.sqrt(varU*x_S2_wing**2 + varv)
        Varphi_S2_wing = Var_phi(mu_S2_wing, sigma_S2_wing)

        Varphi_S1 = np.maximum(Varphi_S1, 0)
        Varphi_S2 = np.maximum(Varphi_S2, 0)
        Varphi_S2_wing = np.maximum(Varphi_S2_wing, 0)

        # check if in S1
        x_0 = Varphi_S1[0]
        x_greater = Varphi_S1[1:]
        x_greater_min = np.maximum(0., np.amin(x_greater))
        if x_0 > x_greater_min:
            in_S1 = False
            #print('Not in S1')
        else:
            in_S1 = True
            #print('In S1')
            # check if the wings are at least as large
            violated_S1 = np.any(np.greater(Varphi_S2_wing, Varphi_S1))
            if violated_S1 == True:
                pass
                #import pdb; pdb.set_trace()
                #print('violated wing condition')
            else:
                pass
                #print('wing condition satisfied')

        # check if in S2
        x_0 = Varphi_S2[-1]
        x_less = Varphi_S2[:-1]
        x_less_min = np.maximum(0., np.amin(x_less))
        if x_0 > x_less_min:
            in_S2 = False
            #print('Not in S2')
        else:
            in_S2 = True
            #print('In S2')
            violated_S2 = np.any(np.greater(Varphi_S1, Varphi_S2_wing))
            if violated_S2 == True:
                pass
                #import pdb; pdb.set_trace()
                #print('violated wing condition')
            else:
                pass
                #print('wing condition satisfied')

        if (in_S1 == True) and (in_S2 == False):
            print('check')
            if violated_S1 == True:
                print('violated wing condition for S1')
                import pdb; pdb.set_trace()

        if (in_S2 == True) and (in_S1 == False):
            print('check')
            if violated_S2 == True:
                print('violated wing condition for S2')
                import pdb; pdb.set_trace()

        if (in_S2 == True) and (in_S1 == True):
            print('check')
            if (violated_S1 == True) and (violated_S2 == True):
                print('violated wing condition for S1 and S2')
                import pdb; pdb.set_trace()
            if (violated_S1 == False) and (violated_S2 == False):
                print ('satisfies both wing conditions simultaneously!')
                # import pdb; pdb.set_trace()

        if (in_S1 == True) and (in_S2 == True):
            in_both = True
        else:
            in_both = False

        if (in_S1 == False) and (in_S2 == False):
            import pdb; pdb.set_trace()
            return False, in_both
        else:
            return True, in_both

if __name__ == '__main__':
    r = np.linspace(-5, 5, 1000)
    g_out = g(r)
    f_out = f(r)

    # plt.figure(1)
    # plt.plot(r, g_out)
    # plt.xlabel('r')
    # plt.ylabel('g(r)')
    # plt.ylim(-5, 5)
    # plt.grid(True)
    # plt.savefig('plots//plot_g.pdf')
    # plt.close()

    # plt.figure(2)
    # plt.plot(r, f_out)
    # plt.xlabel('r')
    # plt.ylabel('f(r)')
    # plt.ylim(0, 5)
    # plt.grid(True)
    # plt.savefig('plots//plot_f.pdf')
    # plt.close()

    # plt.figure(3)
    # plt.scatter(1/r, f_out)
    # plt.xlabel('r')
    # plt.ylabel('f(1/r)')
    # #plt.ylim(0, 5)
    # plt.grid(True)
    # plt.savefig('plots//plot_f_one_over_r.pdf')
    # plt.close()

    # mu = np.linspace(-5, 5, 1000)
    # sigma = 1 #################### this is unrealistic as mu and sigma should be coupled
    # plt.figure(4)
    # Ephi = E_phi(mu, sigma)
    # plt.plot(mu, Ephi)
    # plt.xlabel('$\mu$')
    # plt.ylabel('Expected value of phi for sigma = 1')
    # #plt.ylim(0, 5)
    # plt.grid(True)
    # plt.savefig('plots//plot_Ephi.pdf')
    # plt.close()

    # plt.figure(5)
    # Varphi = Var_phi(mu, 2)
    # plt.plot(mu, Varphi)
    # plt.xlabel('$\mu$')
    # plt.ylabel('Variance of phi for sigma = 1')
    # #plt.ylim(0, 5)
    # plt.grid(True)
    # plt.savefig('plots//plot_Varphi.pdf')
    # plt.close()

    # plt.figure(6)
    # x = np.linspace(-2, 2, 500)
    # muU = 3
    # muv = 3
    # varU = 0.1
    # varv = 0.01
    # mu = muU*x + muv
    # sigma = np.sqrt(varU*x**2 + varv)
    # Varphi = Var_phi(mu, sigma)
    # plt.plot(x, Varphi)
    # plt.xlabel('$x$')
    # plt.ylabel('Variance of phi for some weights')
    # plt.grid(True)
    # plt.savefig('plots//plot_Varphi_of_x.pdf')
    # plt.close()

    # plt.figure(7) # f over g as a function of x
    # r = mu/sigma
    # f_out = f(r)
    # g_out = g(r)
    # plt.scatter(x, g_out/np.sqrt(f_out))
    # plt.xlabel('$x$')
    # plt.ylabel('$\sqrt{f}/g$ for some weights')
    # plt.grid(True)
    # plt.savefig('plots//plot_f_over_g_of_x.pdf')
    # plt.close()

    # plt.figure(8) # true var[phi] versus linearised approx
    # x = np.linspace(-2, 2, 500)
    # muU = 3
    # muv = 3
    # varU = 0.1
    # varv = 0.01
    # mu = muU*x + muv
    # sigma = np.sqrt(varU*x**2 + varv)
    # # true variance
    # Varphi = Var_phi(mu, sigma)
    # varphiline, = plt.plot(x, Varphi, label='Var $[\phi(a)]$')
    # # linearised variance
    # linVarphi = linearised_Var_phi(mu, sigma)
    # linvarphiline, =  plt.plot(x, linVarphi, label='truncated quadratic approximation')
    # plt.xlabel('$x$')
    # plt.ylabel('variance')
    # plt.grid(True)
    # plt.legend(handles = [varphiline, linvarphiline])
    # plt.title('True versus approximate variance when $\mu_U =${}, $\mu_v =${}, $\sigma_U^2 =${}, $\sigma_v^2 =${}'.format(muU, muv, varU, varv))
    # plt.savefig('plots//plot_true_vs_approx_var.pdf')
    # plt.close()

    # compare many linearised and exact non-convex variance contributions
    final_pass_test = True
    count = 0
    for muU in [0]: #[-10, -3, -2, -1, -0.01]:
        for muv in [-10, -3, -2, -1, -0.01]:
            for varU in [1e-10, 0.01, 0.1, 1, 10]:
                for varv in [1e-10, 0.01, 0.1, 1, 10]:
                    pass_test, in_both = compare_var_linearised(muU=muU, muv=muv, varU=varU, varv=varv, title=str(count), check_pass_test=True, plot=True)
                    count = count + 1
                    if in_both == True:
                        pass
                        #print('muU:{}, muv:{}, varU:{}, varv:{}'.format(muU, muv, varU, varv))
                    if pass_test == False:
                        final_pass_test = False
                        # import pdb; pdb.set_trace()
    print('Final check:{}'.format(final_pass_test))

    # muU = 3
    # muv = -3
    # varU = 10
    # varv = 0.01
    # compare_var_linearised(muU=muU, muv=muv, varU=varU, varv=varv, title='sandbox', xrange=[-2, 2])

    # test a few
    # muU = -1
    # muv = 3
    # varU = 1
    # varv = 0.01
    # compare_var_linearised(muU=muU, muv=muv, varU=varU, varv=varv, show=True, xrange=[-200, 200])


import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import scipy.optimize as spo

def f(X):
    Y = (X - 1.5)**2 + 0.5
    print("X: {}, Y: {}".format(X, Y))
    return Y

def p03_minimizer_in_python():
    Xguess = 2.0
    min_result = spo.minimize(f, Xguess, method='SLSQP', options={'disp': True})
    print("Minima found at: X:{}, Y:{}".format(min_result.x, min_result.fun))

def error_func(line, data):
    err = np.sum((data[:, 1] - (line[0]*data[:, 0] + line[1]))**2)
    return err

def fit_line(data, error_func):
    l = np.float32([0, np.mean(data[:, 1])])
    x_ends = np.float32([-5, 5])
    plt.plot(x_ends, l[0] * x_ends + l[1], 'm--', linewidth=2.0, label="initial line")

    result = spo.minimize(error_func, l, args=(data, ), method='SLSQP', options={'disp': True})
    return result.x

def p09_fit_a_line_to_given_data_points():
    l_orig = np.float32([4, 2])
    print("Origin line: C0={}, C1={}".format(l_orig[0], l_orig[1]))
    Xorig = np.linspace(0, 10, 21)
    Yorig = l_orig[0]*Xorig + l_orig[1]
    plt.plot(Xorig, Yorig, 'b--', linewidth=2.0, label="Original line")

    noise_sigma = 3.0
    noise = np.random.normal(0, noise_sigma, Yorig.shape)
    data = np.asarray([Xorig, Yorig + noise]).T
    plt.plot(data[:, 0], data[:, 1], 'go', label="Data points")

    l_fit = fit_line(data, error_func)
    print("Fitted line: C0={}, C1={}".format(l_fit[0], l_fit[1]))
    plt.plot(data[:, 0], l_fit[0]*data[:, 0] + l_fit[1], 'r--', linewidth=2.0, label="fit line")

    plt.legend(loc="upper left")
    plt.show()

def error_poly(C, data):
    err = np.sum((data[:, 1] - np.polyval(C, data[:, 0])) ** 2)
    return err

def fit_poly(data, error_func, degree=3):
    Cguess = np.poly1d(np.ones(degree+1, dtype=np.float32))

    x=np.linspace(-5, 5, 21)
    plt.plot(x, np.polyval(Cguess, x), 'm--', linewidth=2.0, label="initial guess")

    result = spo.minimize(error_func, Cguess, args=(data, ), method='SLSQP', options={'disp': True})

    return np.poly1d(result.x)

def test_run():
    p09_fit_a_line_to_given_data_points()

if __name__ == '__main__':
    test_run()


import numpy as np

# CRIAÇÃO DE DOIS ARRAYS X E Y
print("\nCRIAÇÃO DE DOIS ARRAYS X E Y!!\n")
x = np.ones((2, 2))
y = np.eye(2)
print("x : \n", x)
print('y: \n', y)

# SOMA
print('\n***SOMA***\n')
print('soma de dois arrays: \n', x + y)
print('soma com float/int: \n', x + 2) #broadcasting (ADD  2 em todas as posições de X)

# SUBTRAÇÂO
print('\n***SUBTRAÇÃO***\n')
print('subtração de dois arrays:\n', x - y)
print('subtração com float/int:  \n', x -2) #broadcasting (subtrai 2 de todos os elementos de X)

# divisão
print('\n***DIVISÃO***\n')
print('divisão  de dois arrrays: \n', x / y)
print('divisão com float/int: \n ', x / 2) # broadcasting

'''
quando o boradcasting não funciona!
np.array([1, 2, 3]) + np.array([1, 2])

* Dimensões diferentes array inconsistente.

** np.array([1, 2, 3]) + np.array([1, 2, 3])
    ^\ assim funcionaria pois tem as mesmas dimensoes.

    ***np.array([1, 2, 3]) + np.array([1])
       ^\ assim funcionaria pois adicionaria um
       a todas as posições.
'''

# soma subtração e divisão
print('\n***SOMA SUBTRAÇÃO E DIVISÃO***\n')
print('combinação de operações: \n', (x + y) / (x-2))


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

def generateDataPoints(n):
    x = []
    xi = np.zeros(5)
    xi[0] = 1
    for i in range(0,n):
        for j in range(1,5):
            xi[j] = math.floor(np.random.uniform(0,100))
        x.append(xi)
        xi = np.zeros(5)
        xi[0] = 1
    return np.asarray(x)

def generateConstants():
    c = np.zeros(5)
    for i in range(0,5):
        c[i] = math.floor(np.random.uniform(0,10))
    return np.asarray(c)

def generateErrors(n):
    e = np.zeros(n)
    for i in range(0,n):
        e[i] = np.random.random()
    return np.asarray(e)

def computeYi(x,c,e,n):
    y = np.zeros(n)
    for i in range(0,n):
        k = np.atleast_2d(x[i])
        l = np.dot(c,k.T)
        y[i] = l[0] + e[i]
    return y

def BGD(alpha,x,y,iterate,n):
    cn = np.ones(5)
    x_transpose = x.T
    for i in range(0,iterate):
        h = np.dot(x,cn)
        loss = h - y
        tmp = np.atleast_2d(loss)
        #Jc = np.sum(np.dot(tmp,tmp.T)) / (2*n)
        gradient = np.dot(x_transpose, loss)/n
        cn = cn - alpha*gradient
    return cn

def closedForm(x,y):
    x_transpose = x.T
    ccf = np.dot(np.dot(np.linalg.inv(np.dot(x_transpose,x)),x_transpose),y)
    return ccf

print("Due to high amount of iterations the solution may take around 30 secs. to print. Please be patient")
# Generating 5 data sets, each comprising 102 sample points
xp = []
n = 102
for i in range(0,5):
    xp.append(generateDataPoints(n))

# Generating initial constants based on which y is calculated
c = generateConstants()

# Generating errors based on normal distribution
e = generateErrors(n)

# Computing y based on the above equations
yp = []
for i in range(0,5):
    yp.append(computeYi(xp[i],c,e,n))

# Running batch gradient on the data set
alpha = 0.0001
iteration = 200000
cn = []
for i in range(0,5):
    cn.append(BGD(alpha,xp[i],yp[i],iteration,n))

# Computing closed form solution
ccf = []
for i in range(0,5):
    ccf.append(closedForm(xp[i],yp[i]))

print("Initial assumption for constants is : " +str(c))
print("Closed form solution is : " + str(np.mean(ccf,axis = 0)))
print("Solution through batch gradient is : "+str(np.mean(cn,axis = 0)))


import numpy as np
import cvxpy as cp
import matplotlib.pyplot as plt
import warnings
warnings.filterwarnings("ignore")

omega0 = [0.00696628, -0.03152652, -0.01437158, 0.02673555, 0.04050866, 0.01683239,
          -0.03949126, -0.01910853, 0.00495693, 0.02092892, -0.00496835, 0.00556729,
          -0.02365522, 0.01294543, 0.02428625, -0.00026533, -0.0335113, 0.03062016,
          -0.01458504, -0.00886471]

def main():
    data_x = np.loadtxt('./HW2-Data/X.csv', delimiter=",")    # (1000, 20)
    data_y = np.loadtxt('./HW2-Data/y.csv', delimiter=",")    # (1000,)
    np.random.seed(42)

    test_time = 100
    results = []
    lambs = []
    for i in range(test_time):
        lam = 0.0002 * i
        # Construct the problem.
        x = cp.Variable(data_x.shape[1])
        objective = cp.Minimize(cp.norm(data_x * x - data_y, 2) ** 2 + lam * cp.norm(x, 1))
        constraints = None
        prob = cp.Problem(objective, constraints)

        # The optimal objective value is returned by `prob.solve()`.
        result = prob.solve()
        # The optimal value for x is stored in `x.value`.
        omega = x.value
        # res = np.linalg.norm(x=np.array(omega) - np.array(omega0), ord=2)
        res = len([ele for ele in omega if ele > (1 / 10**5)])
        results.append(res)
        lambs.append(lam)
    plt.plot(lambs, results)
    plt.xlabel('lambda')
    plt.ylabel('number of non-zero elements in the w∗')
    plt.show()
    print(lambs[results.index(min(results))])

if __name__ == '__main__':
    x1 = [-1.4061302594060445e-10, -4.65884647883577e-10, -1.656308226207918e-11, 1.0783624798375546e-09,
          0.010999979587224356, 5.443627077995256e-10, -2.4079179205561544e-10, -1.8217885680129265e-10,
          1.020317134648606e-10, 3.373589208991034e-10, 1.3363294577100737e-10, 1.6664435256944309e-10,
          -9.81998777663763e-11, 1.970396336241177e-10, 3.259584591576223e-10, -4.037271903035527e-11,
          -6.594393656999212e-10, -1.077694957500913e-10, -1.519109563741851e-09, -2.060329595971895e-09]
    x2 = [-2.491275254088687e-11, -1.1045703755170562e-10, -1.3075413915069995e-11, 1.7732233012580192e-10, 0.010955734757143745, 7.291304871759226e-11, -7.303131999374966e-11, -4.08660928720432e-11, 1.8747491645179315e-11, 7.3210736507542e-11, 2.7289142002056215e-11, 3.408178649590049e-11, -1.3934929176360982e-11, 4.4162058079090685e-11, 8.306344564321131e-11, -5.809687082843339e-13, -1.1984975941868357e-10, -4.3405526269429055e-12, -2.728128725700397e-10, -3.8536934588518413e-10]

    data_x = np.loadtxt('./HW2-Data/X.csv', delimiter=",")  # (1000, 20)
    data_y = np.loadtxt('./HW2-Data/y.csv', delimiter=",")  # (1000,)
    np.random.seed(42)

    lam = 0.1
    # Construct the problem.
    x = cp.Variable(data_x.shape[1])
    objective = cp.Minimize(cp.norm(data_x * x - data_y, 2) ** 2 + lam * cp.norm(x, 'inf'))
    constraints = None
    prob = cp.Problem(objective, constraints)

    # The optimal objective value is returned by `prob.solve()`.
    result = prob.solve()
    # The optimal value for x is stored in `x.value`.
    omega = x.value
    print(list(omega))


"""

https://en.wikipedia.org/wiki/Legendre_polynomials

"""

from sympy import *
from sympy.abc import *
import sys
import os

def legendre(n):
    if n < 0:
        return 0
    p = 0
    for k in range(n+1):
        p += binomial(n, k) * binomial(n+k, k) * ((x-1)/2)**k
    return expand(simplify(p))

def shifted_legendre(n):
    if n < 0:
        return 0
    p = 0
    for k in range(n+1):
        p += binomial(n, k) * binomial(n+k, k) * (-x)**k
    return expand(simplify(-1**n * p))

def associated_legendre(m, l):
    p = 0
    for k in range(m, l+1):
        p += factorial(k)/factorial(k-m) * x**(k-m) * binomial(l, k) * binomial((l+k-1)/2, l)
    return expand(simplify(-1**m * 2**l * (1-x*x)**(m/2) * p))

if len(sys.argv) < 2:
    print("usage: order ...")
    sys.exit()

for i in range(len(sys.argv)-1):
    n = int(sys.argv[i+1])
    print("Order {}".format(n))
    print("Legendre")
    print(legendre(n))
    print("Shifted Legendre")
    print(shifted_legendre(n))
    for i in range(n+1):
        print("Associated Legendre {} {}".format(i, n))
        print(associated_legendre(i, n))
    print()


from scipy import linalg as la
from scipy import sparse as spar
from scipy.sparse import linalg as sla
import scipy as sp

def Problem3(n):
    return la.toeplitz([2,-1]+[0]*(n-2), [2,-1]+[0]*(n-2))

def Problem4(n):
    diags = sp.array([[-1]*n, [2]*n, [-1]*n])
    return spar.spdiags(diags, [-1,0,1], n, n,format='csr')

def Problem5(n, sparse=False):

    b = sp.rand(n,1)

    if sparse is True:
        A=Problem4(n)
        return sla.spsolve(A,b)
    else:
        A=Problem3(n)
        return la.solve(A,b)

def Problem6(n):
    A = Problem4(n)
    eig = sla.eigs(A.asfptype(), which="SM")[0].min()
    return eig*(n**2)


from numpy import genfromtxt
import numpy as np
from scipy.spatial import distance_matrix
import heapq

k = 3
temp = genfromtxt('Q2Q3_input.csv', delimiter=',')
inp = temp[1:, 1:]

#create a distance matrix
mat = distance_matrix(inp, inp, p=2)
k_nearest = mat.argsort()[:, :k+1][:, k] #Indices of k-nearest neigbours
dist_k_o = mat[np.arange(695), k_nearest] #Slice to get distances

#Code to calculate nko
n_k_o = []
for i in range(695):
	n_i_o = []
	for j in range(695):
		if (mat[i, j] <= dist_k_o[i]) and (i != j):
			n_i_o.append(j)
	n_k_o.append(n_i_o)

def reachdist_k_o(o, o_d):
	return max(dist_k_o[o], mat[o, o_d])

#Code to calculate lrd
lrd = []
print(reachdist_k_o(0,1))
for i in range(695):
	lrd.append(len(n_k_o[i]) / sum([reachdist_k_o(j, i) for j in n_k_o[i]]))

#Code to calculate lof
lof = []
for i in range(695):
	lof.append(sum([lrd[j] / lrd[i] for j in n_k_o[i]]) / len(n_k_o[i]))

#Print outliers
outliers = heapq.nlargest(5, range(len(lof)), lof.__getitem__)
print(outliers)
print([lof[i] for i in outliers])


from lib import Simulation
import numpy as np
from numpy import mean, min, max, median, quantile, sqrt
from matplotlib import pyplot as plt
from scipy.stats import expon, norm, erlang
from time import time
from math import factorial as fact

def std(values):
	return np.std(values, ddof=1)

def mean_confidence_asymptotic(values, confidence):
	# when variables iid and high n
	n = len(values)
	z = norm.ppf((1 + confidence) / 2)
	s = std(values)
	m = mean(values)
	j = m - z * s / sqrt(n)
	k = m + z * s / sqrt(n)
	return (j, k)

λ = 10
µ = 15
c = 1  # number of servers
max_time = 1000  # 2000 / µ
debug_interval = max_time
Ntr = 200  # number of simulations to run

# RUN SIMULATIONS
simulations = []
for i in range(Ntr):
	if i % 50 == 0:
		print(f"Running simulation {i}")
	sim = Simulation(max_time, λ, µ, c, debug_interval)
	sim.run()
	simulations.append(sim)
print(f'Finished simulating')

# 11: calculate expected avg q_time at the end
ρ = λ / (c * µ)
pi_0 = 1 / (sum([(c * ρ)**k / fact(k) for k in range(0, c)]) + (c * ρ)**c / (fact(c) * (1 - ρ)))
pi_c_plus = (c * ρ)**c / (fact(c) * (1 - ρ)) * pi_0
theor_avg_q_time = ρ / (λ * (1 - ρ)) * pi_c_plus

values = [mean(sim.q_times) for sim in simulations]
interval = mean_confidence_asymptotic(values, 0.95)
print(f'theor avg q time: {theor_avg_q_time: .4g}, empirical: {mean(values): .4g} with CI at 0.95: [{interval[0]: .4g} - {interval[1]: .4g}]')


# This file is part of the mt5se package
#  mt5se home: https://github.com/paulo-al-castro/mt5se
# Author: Paulo Al Castro
# Date: 2020-11-17

import pandas as pd
import numpy as np
from math import sqrt
import statistics
"""
    calcGeoAvgReturn(returns::Array{Float64} [,n::Int] )
 returns the geometric average return of the series of the n first returns returns. If n is not informed the whole array is used
"""
def calcGeoAvgReturn(returns,n=None):
   ret=1
   if n==None:
      n=len(returns)
   for  i in range(n):
      ret*=(1+returns[i])

   return ret**(1.0/n)-1

"""
    calcTotalReturn(returns::Array{Float64})
   returns the Total return of a series of returns
   If the size is provided it considers just 'size' more recent (more to the right) returns
"""
def calcTotalReturn(returns,size=None):
   ret=1
   s=len(returns)
   if size==None:
      size=0
   elif size<s:
      size=s-size
   else:
      size=0 # it gets in maximum the return of the total number of elements
   for  i in range(s-1,size-1,-1):
      ret*=(1+returns[i])
   return ret-1

def changedSignal(returns):
   s=len(returns)
   if s==None or s==1:
      return False
   for i in range(s-1):
      if returns[i]*returns[i+1]<0:
         return True
   return False

"""
    calcAvgReturn(returns::Array{Float64})
    returns the arithmetic average return of the series of returns
"""
def calcAvgReturn(returns):
   sum=float(0)
   s=len(returns)
   for  i in range(s):
      sum+=returns[i]
   return sum/s

"""
    calcAnnualReturn(returns::Array{Float64}, numberOfDays)
    returns the equivalent annual return for the given serie of returns. The numberOfDays informs the number of working days in the serie, and it
         can have more or less than a year. One year is assumed to have 252 [working] days.
"""
def calcAnnualReturn(returns, numberOfDays):
   gReturn=calcTotalReturn(returns)
   return (1+gReturn)**(252.0/numberOfDays)-1

"""
    calcAnnualSR(returns::Array{Float64}, riskfree, numberOfDays)
    returns the equivalent annual sharpe ratio (SR) for the given serie of returns and risk free rate. The numberOfDays informs the number of working days in the serie, and it
         can have more or less than a year. One year is assumed to have 252 [working] days.
"""
def calcAnnualSR(returns, riskfree, numberOfDays):
   # implemented according paper: Andrew W Lo},The Statistics of Sharpe Ratios, journal = {Financial Analysts Journal}, 2003

   return sqrt(252)*calcSR(returns,riskfree)

"""
    calcStdDev(x::Array{Float64})
    return the standard deviation of a sample
"""
def calcStdDev(x):
   return statistics.stdev(x)

"""
    calcSR(returns::Array{Float64}, riskfree)
    returns the Sharpe ratio (SR) for the given serie of returns and risk free rate.
"""
def calcSR(returns, riskfree):
   avg=calcAvgReturn(returns)
   sigma=calcStdDev(returns)
   if sigma!=0:
       return (avg-riskfree)/sigma
   return -1

"""
    calculates a serie of return given a serie of prices as argument
    return[i]=price[i]/price[i-1]-1
    the serie of returns has length equal to the price serie lenth minus 1.
"""
def calcReturns(serie):
    x=[]
    for i in range(len(serie)-1): # calculates the serie of returns
        x.append(serie[i+1]/serie[i]-1)
    return x

"""
   gives the standard deviation of returns given a serie of prices
"""
def calcStdDevFromPrice(x):
    returns=calcReturns(x)
    return calcStdDev(returns)

"""
   gives the average returns given a serie of prices
"""
def calcAvgReturnFromPrice(x):
    returns=calcReturns(x)
    return calcAvgReturn(returns)


from scipy.stats import binom, norm
import numpy as np
import matplotlib.pyplot as plt
plt.rcParams["figure.figsize"] = (15,6)
plt.rcParams.update({'font.size': 12})

class Binomial:

    """
    A class to represent the Binomial Distribution.

    ...

    Attributes
    ----------
    probabilities : list
        List of probabilities for each bernoulli trial
    mean : float
        Average number of occurences based on the binomial distribution
    var : float
        Variance of the binomial probability distribution

    Methods
    -------
    pmf(x,n,p,visualize,fill_color):
        Returns the probability mass function of the distribution
    cdf(x,n,p,visualize=True,fill_color,upper):
        Returns the cumulative density function for the number within the number of trials in the distribution
    cdf2(x,n,p,visualize=True,fill_color):
        Returns the cumulative density function for the interval within the number of trials in the distribution
    """

    def __init__(self):
        self.probabilities = None
        self.mean = None
        self.var = None

    def pmf(self,x,n,p,visualize=True,fill_color='orange'):

        '''
        Return the Probability Mass Function for a given number of trials

            Parameters:
                    x (int): Binomial discrete value
                    n (int): Number of trials
                    p (float): Probability of the occurence of an event
                    visualize (bool): Boolean variable to visualize the distribution plot
                    fill_color (str): color of the mass function

            Returns:
                    pmf (float): Returns the probability mass function for a given number for the specified number of bernoulli trials

        '''

        r_values = list(range(n + 1))
        self.mean, self.var = binom.stats(n, p)

        self.probabilities = [binom.pmf(r, n, p) for r in r_values]

        prob_x = binom.pmf(x, n, p)

        if visualize:
            bars = plt.bar(r_values, self.probabilities)
            bars[x].set_color(fill_color)
            plt.xticks(r_values)
            plt.xlabel('x')
            plt.ylabel('Probability')
            plt.title("Probability Mass Function")
            plt.show()

        return prob_x

    def cdf(self,x,n,p,visualize=True,fill_color='orange',upper=False):

        '''
        Returns the Cumulative Density Function for a given number of trials

            Parameters:
                    x (int): Binomial discrete value
                    n (int): Number of trials
                    p (float): Probability of the occurence of an event
                    visualize (bool): Boolean variable to visualize the distribution plot
                    fill_color (str): color of the mass function
                    upper (bool): Boolean variable to indicate upper half of the function

            Returns:
                    cdf (float): Returns the cumulative density function for a given number for the specified number of bernoulli trials

        '''

        r_values = list(range(n + 1))
        self.mean, self.var = binom.stats(n, p)

        self.probabilities = [binom.cdf(r, n, p) for r in r_values]

        prob_x = binom.cdf(x, n, p) if not upper else 1-binom.cdf(x, n, p)

        if visualize:
            bars = plt.bar(r_values, self.probabilities)
            if not upper:
                for i in range(x+1):
                    bars[i].set_color(fill_color)
            else:
                for i in range(x+1,n+1):
                    bars[i].set_color(fill_color)
            plt.xticks(r_values)
            plt.ylabel('x')
            plt.ylabel('Probability')
            plt.title("Cumulative Density Function")
            plt.show()

        return prob_x

    def cdf2(self,x,n,p,visualize=True,fill_color='orange'):

        '''
        Returns the Cumulative Density Function for a given number of trials between two specified values

            Parameters:
                    x (list): interval between which the cdf needs to be calculated e.g. 3<x<=8 [4,8]
                    n (int): Number of trials
                    p (float): Probability of the occurence of an event
                    visualize (bool): Boolean variable to visualize the distribution plot
                    fill_color (str): color of the mass function
                    upper (bool): Boolean variable to indicate upper half of the function

            Returns:
                    cdf2 (float): Returns the cumulative density function for the given interval for the specified number of bernoulli trials

        '''

        r_values = list(range(n + 1))
        self.mean, self.var = binom.stats(n, p)

        self.probabilities = [binom.cdf(r, n, p) for r in r_values]

        try:
            prob_x = binom.cdf(x[1], n, p) - binom.cdf(x[0], n, p)

            if visualize:
                bars = plt.bar(r_values, self.probabilities)
                for i in range(x[0],x[1]+1):
                    bars[i].set_color(fill_color)
                plt.xticks(r_values)
                plt.xlabel('x')
                plt.ylabel('Probability')
                plt.title("Cumulative Density Function")
                plt.show()

            return prob_x

        except TypeError:
            raise TypeError("Input Parameter must be a list")

class Gaussian:

    """
    A class to represent the Gaussian/Normal Distribution.

    ...

    Attributes
    ----------
    None

    Methods
    -------
    cdf(x,n,p,visualize=True,fill_color,upper):
        Returns the cumulative density function for the area under the curve of a normal distribution
    cdf2(x,n,p,visualize=True,fill_color):
        Returns the cumulative density function for the area under the curve within the interval of the normal distribution
    ppf(self,prob,mu,sigma,visualize,fill_color):
        Returns the percent point function or the inverse of the cdf
    """

    def __init__(self):
        pass

    def cdf(self,x,mu,sigma,visualize=True,fill_color='lightblue',upper=False):

        '''
        Returns the Cumulative Density Function for a given continous variable

            Parameters:
                    x (float): continous value
                    mu (float): Mean of the Gaussian Distribution
                    sigma (float): standard deviation of the Gaussian Distribution
                    visualize (bool): Boolean variable to visualize the distribution plot
                    fill_color (str): Shaded color of the area under the curve
                    upper (bool): Boolean variable to indicate upper half of the function

            Returns:
                    cdf (float): Returns the cumulative density function for the area under the curve for a given continous variable

        '''

        prob = norm.cdf(x, mu, sigma) if not upper else 1-norm.cdf(x, mu, sigma)

        if visualize:
            s = np.random.normal(mu, sigma, 1000)
            s.sort()
            x = np.arange(s[0], x, 0.01) if not upper else np.arange(x, s[-1], 0.01)
            plt.plot(s, norm.pdf(s, mu, sigma))
            plt.xlabel('x')
            plt.ylabel('Probability')
            plt.title("Cumulative Density Function")
            plt.fill_between(x, norm.pdf(x, mu, sigma),color=fill_color)
            plt.show()

        return prob

    def cdf2(self,x,mu,sigma,visualize=True,fill_color='lightblue'):

        '''
        Returns the Cumulative Density Function for the interval between two continous variables

            Parameters:
                    x (list): A list of two continous values
                    mu (float): Mean of the Gaussian Distribution
                    sigma (float): standard deviation of the Gaussian Distribution
                    visualize (bool): Boolean variable to visualize the distribution plot
                    fill_color (str): Shaded color of the area under the curve

            Returns:
                    cdf2 (float): Returns the cumulative density function for the area under the curve for a given interval of continous variables

        '''

        prob1 = norm.cdf(x[0], mu, sigma)
        prob2 = norm.cdf(x[1], mu, sigma)
        prob = prob2 - prob1

        if visualize:
            s = np.random.normal(mu, sigma, 1000)
            s.sort()
            x = np.arange(x[0], x[1], 0.01)
            plt.plot(s, norm.pdf(s, mu, sigma))
            plt.xlabel('x')
            plt.ylabel('Probability')
            plt.title("Cumulative Density Function")
            plt.fill_between(x, norm.pdf(x, mu, sigma),color=fill_color)
            plt.show()

        return prob

    def ppf(self,prob,mu,sigma,visualize=True,fill_color='lightblue'):

        '''
        Returns the Percent Point Function or the Inverse CDF

            Parameters:
                    x (float): probability value
                    mu (float): Mean of the Gaussian Distribution
                    sigma (float): standard deviation of the Gaussian Distribution
                    visualize (bool): Boolean variable to visualize the distribution plot
                    fill_color (str): Shaded color of the area under the curve

            Returns:
                    ppf (float): Returns the percent point function or the inverse cdf

        '''

        val = norm.ppf(prob, mu, sigma)

        if visualize:
            s = np.random.normal(mu, sigma, 1000)
            s.sort()
            x = np.arange(s[0], val, 0.01)
            plt.plot(s, norm.pdf(s, mu, sigma))
            plt.xlabel('x')
            plt.ylabel('Probability')
            plt.title("Percent Point Function")
            plt.fill_between(x, norm.pdf(x, mu, sigma),color=fill_color)

        return val


## Follow Luke Gloege's method
## https://towardsdatascience.com/the-correct-way-to-average-the-globe-92ceecd172b7

def earth_radius(lat):
    '''
    calculate radius of Earth assuming oblate spheroid
    defined by WGS84

    Input
    ---------
    lat: vector or latitudes in degrees

    Output
    ----------
    r: vector of radius in meters

    Notes
    -----------
    WGS84: https://earth-info.nga.mil/GandG/publications/tr8350.2/tr8350.2-a/Chapter%203.pdf
    '''
    from numpy import deg2rad, sin, cos,arctan,tan

    # define oblate spheroid from WGS84
    a = 6378137
    b = 6356752.3142
    e2 = 1 - (b**2/a**2)

    # convert from geodecic to geocentric
    # see equation 3-110 in WGS84
    lat = deg2rad(lat)
    lat_gc = arctan( (1-e2)*tan(lat) )

    # radius equation
    # see equation 3-107 in WGS84
    r = (
        (a * (1 - e2)**0.5)
         / (1 - (e2 * cos(lat_gc)**2))**0.5
        )

    return r

def area_grid(lat, lon):
    """
    Calculate the area of each grid cell
    Area is in square meters

    Input
    -----------
    lat: vector of latitude in degrees
    lon: vector of longitude in degrees

    Output
    -----------
    area: grid-cell area in square-meters with dimensions, [lat,lon]

    Notes
    -----------
    Based on the function in
    https://github.com/chadagreene/CDT/blob/master/cdt/cdtarea.m
    """
    from numpy import meshgrid, deg2rad, gradient, cos
    from xarray import DataArray

    xlon, ylat = meshgrid(lon, lat)
    R = earth_radius(ylat)

    dlat = deg2rad(gradient(ylat, axis=0))
    dlon = deg2rad(gradient(xlon, axis=1))

    dy = dlat * R
    dx = dlon * R * cos(deg2rad(ylat))

    area = dy * dx

    xda = DataArray(
        area,
        dims=["lat", "lon"],
        coords={"lat": lat, "lon": lon},
        attrs={
            "long_name": "area_per_pixel",
            "description": "area per pixel",
            "units": "m^2",
        },
    )
    return xda


# -*- coding: utf-8 -*-
'''
	⎡f(n+1) f(n)⎤ = ⎡1 1⎤^n
	⎣f(n) f(n-1)⎦	⎣1 0⎦
'''
import numpy
import math
import time

start = [[1,1],[1,0]]

def fibonacci_sequence(n):
   if n <= 1:
       return n
   else:
       return(fibonacci_sequence(n-1) + fibonacci_sequence(n-2))

def fibonacci_sequence_divide(n):
	if n == 1:
		return start
	if n == 0:
		return 1
	y = fibonacci_sequence_divide(math.floor(n/2))
	z = numpy.dot(y,y)
	return numpy.dot(z,fibonacci_sequence_divide(n-2*math.floor(n/2)))

t = time.time()
print fibonacci_sequence_divide(30)[0][1]
print time.time() - t

t = time.time()
print fibonacci_sequence(30)
print time.time() - t


import numpy as np
import copy
import scipy
import scipy.linalg

def ResolverCosasProgresivas(a,b):
    a = np.array(a)
    n = len(b)
# Eliminación
    for j in range(0,n-1): # colunmas
        for i in range(j+1,n): # renglones
            if a[i,j] != 0.0:
                lam = a[i,j]/a[j,j]
                L[i][j] = lam
                a[i,j:n] = a[i,j:n] - lam*a[j,j:n]
                b[i] = b[i] - lam*b[j]
    for i in range (0, n-1):
        b[i] = (b[i] - np.dot(a[i,i+1:n], b[i+1:n]))/a[i,i]
    return b

def ResolverCosasRegresivas(a,b):
    a = np.array(a)
    n = len(b)
# Eliminación
    for j in range(0,n-1): # colunmas
        for i in range(j+1,n): # renglones
            if a[i,j] != 0.0:
                lam = a[i,j]/a[j,j]
                L[i][j] = lam
                a[i,j:n] = a[i,j:n] - lam*a[j,j:n]
                b[i] = b[i] - lam*b[j]
    # Sustitución regresiva
    for i in range(n-1,-1,-1):
        b[i] = (b[i] - np.dot(a[i,i+1:n],b[i+1:n]))/a[i,i]
    return b

def LU(a):
    L = [[0 for i in range(len(a))]for j in range(len(a))] #inicializar matriz de ceros para la trinagular inferior
    for i in range(0,len(L)):
        L[i][i] = 1

    n = len(a)
# Eliminación
    for j in range(0,n-1): # colunmas
        for i in range(j+1,n): # renglones
            if a[i,j] != 0.0:
                lam = a[i,j]/a[j,j]
                L[i][j] = lam
                a[i,j:n] = a[i,j:n] - lam*a[j,j:n]
    return L, a

def Imprime(M):
    for l in M:
        print(l)
    print("\n")

if __name__ == "__main__":

    a = np.array([[1,-3,7], [0,3,-2], [-2,6,1.]])
    a_original = copy.deepcopy(a)
    a_inv = []

    I = np.identity(len(a))

    L = []

    L, a = LU(a)

    Imprime(L)
    Imprime(a)
    print (f"{np.dot(L,a)}")

    for i in range(0,len(a)):
        b = I[i]
        y = ResolverCosasProgresivas(L,b)
        x = ResolverCosasRegresivas(a,y)
        a_inv.append(x.T)

    a_inv = np.array(a_inv).T
    print(f"A inversa es \n")
    Imprime (a_inv)

    print (f"Comprobacion A * A^-1 \n{np.dot(a_original, a_inv)}")

#%%


# Zeros and Ones "https://www.hackerrank.com/challenges/np-zeros-and-ones/problem"

import numpy as np

dim = tuple(map(int, input().split()))

print(np.zeros(dim, dtype=np.int))
print(np.ones(dim, dtype=np.int))


# By considering the terms in the Fibonacci sequence
# whose values do not exceed four million,
# find the sum of the even-valued terms.

import numpy as np
import math

def naive_fib(n):
    # recursion
    if n == 1 or n == 2:
        return 1
    else:
        return naive_fib(n - 1) + naive_fib(n - 2)

def dp_fib(n):
    """
    Return the nth Fibonacci number
    :param n: int
    :return: int
    """
    memo = [0 for _ in range(n + 1)]
    memo[1], memo[2] = 1, 1
    for i in range(3, n + 1):
        memo[i] = memo[i - 1] + memo[i - 2]
    return memo[n]

def mat_fib(n):
    # divide and conquer
    base = np.array([[1, 1], [1, 0]])
    ret = base
    for _ in range(n):
        ret = base @ ret
    print(ret)
    return ret[1][1]

def closed_form_fib(n):
    return int(round((1 / math.sqrt(5)) * ((1 + math.sqrt(5)) / 2) ** n -
                     (1 / math.sqrt(5)) * ((1 - math.sqrt(5)) / 2) ** n))

def closed_form_even_fib(n):
    return int((5 + 2 * math.sqrt(5)) / 5 * (2 + math.sqrt(5)) ** n +
               (5 - 2 * math.sqrt(5)) / 5 * (2 - math.sqrt(5)) ** n)

def print_fib(n):
    memo = [0 for _ in range(n + 1)]
    memo[1], memo[2] = 1, 1
    print(memo[1], memo[2], end=" ")
    for i in range(3, n + 1):
        memo[i] = memo[i - 1] + memo[i - 2]
        print(memo[i], end=" ")
    print()

def print_even_fib(n):
    memo = [0 for _ in range(n + 1)]
    memo[1], memo[2] = 1, 1
    for i in range(3, n + 1):
        memo[i] = memo[i - 1] + memo[i - 2]
        if memo[i] % 2 == 0:
            print(memo[i], end=" ")
    print()

def find_even_fib_sum():
    base = np.array([[1, 1], [1, 0]])
    tmp = base
    ret = 0
    while tmp[1][1] < 4000000:
        tmp = base @ tmp
        if tmp[1][1] % 2 == 0:
            ret += tmp[1][1]
    return ret

def find_even_fib_sum2():
    i, ret = 0, 0
    while closed_form_even_fib(i) < 4000000:
        ret += closed_form_even_fib(i)
        i += 1
    return ret

if __name__ == "__main__":
    print(f"The sum of even Fibonacci numbers under 4 million is {find_even_fib_sum2()}")