import math
from typing import Union

import numpy as np

def unit_vector(vector: np.ndarray):
    """ Returns the unit vector of the vector. """
    return vector / np.linalg.norm(vector)

def angle_between_two_vectors_2d(v1: Union[list, tuple, np.ndarray], v2: Union[list, tuple, np.ndarray]) -> float:
    """ Returns the angle in radians between vectors 'v1' and 'v2' """
    # v1_u = v1 / np.linalg.norm(v1)
    # v2_u = v2 / np.linalg.norm(v2)
    # r = np.arccos2(np.clip(np.dot(v1_u, v2_u), -1.0, 1.0))

    ang1 = np.arctan2(*np.array(v1)[::-1])
    ang2 = np.arctan2(*np.array(v2)[::-1])
    r = (ang1 - ang2) % (2 * np.pi)

    return r

def _test_angle_between_two_vectors():
    v1 = [0, 10]
    v2 = [10, 0]
    assert angle_between_two_vectors_2d(v1, v2) == np.pi / 2

    v1 = [1, 0]
    v2 = [-1, -1]
    print(angle_between_two_vectors_2d(v2, v1))

def rotation_meshgrid(xx: np.ndarray, yy: np.ndarray, theta_in_radians: float):
    """Rotate a mesh grid by angle theta_in_radians.

    :param xx: x values of a mesh grid.
    :param yy: y values of a mesh grid.
    :param theta_in_radians: the angle to be rotated.
    :return (xx2, yy2): rotated arrays.
    """

    # Clockwise, 2D rotation_meshgrid matrix
    R = rotation_matrix(theta_in_radians)

    return np.einsum('ji, mni -> jmn', R, np.dstack([xx, yy]))

def rotation_matrix(theta_in_radians: float):

    # Clockwise, 2D rotation_meshgrid matrix

    R = np.array(
        [
            [np.cos(theta_in_radians), np.sin(theta_in_radians)],
            [-np.sin(theta_in_radians), np.cos(theta_in_radians)]]
    )

    return R

def _test_rotation_meshgrid():

    import matplotlib.pyplot as plt

    x_span = np.arange(-10, 11, 1)
    y_span = np.arange(-10, 11, 1)
    theta = 45/180*3.1415926

    xx_1, yy_1 = np.meshgrid(x_span, y_span)

    xx_2, yy_2 = rotation_meshgrid(xx_1, yy_1, theta)

    fig, (ax1, ax2) = plt.subplots(nrows=2)

    ax1.contourf(xx_1, yy_1, np.ones_like(xx_1))
    ax1.set_aspect('equal')
    ax1.set_xlim((-20, 20))
    ax1.set_ylim((-20, 20))

    ax2.contourf(xx_2, yy_2, np.ones_like(xx_2)*2)
    ax2.set_aspect('equal')
    ax2.set_xlim((-20, 20))
    ax2.set_ylim((-20, 20))

    x, y = rotation_meshgrid([10], [0], theta)

def find_line_segment_intersection_1(pt1, pt2, ptA, ptB):
    """ this returns the intersection of Line(pt1,pt2) and Line(ptA,ptB)

        returns a tuple: (xi, yi, valid, r, s), where
        (xi, yi) is the intersection
        r is the scalar multiple such that (xi,yi) = pt1 + r*(pt2-pt1)
        s is the scalar multiple such that (xi,yi) = pt1 + s*(ptB-ptA)
            valid == 0 if there are 0 or inf. intersections (invalid)
            valid == 1 if it has a unique intersection ON the segment    """

    DET_TOLERANCE = 0.00000001

    # the first line is pt1 + r*(pt2-pt1)
    # in component form:
    x1, y1 = pt1
    x2, y2 = pt2
    dx1 = x2 - x1
    dy1 = y2 - y1

    # the second line is ptA + s*(ptB-ptA)
    x, y = ptA;   xB, yB = ptB
    dx = xB - x;  dy = yB - y

    # we need to find the (typically unique) values of r and s
    # that will satisfy
    #
    # (x1, y1) + r(dx1, dy1) = (x, y) + s(dx, dy)
    #
    # which is the same as
    #
    #    [ dx1  -dx ][ r ] = [ x-x1 ]
    #    [ dy1  -dy ][ s ] = [ y-y1 ]
    #
    # whose solution is
    #
    #    [ r ] = _1_  [  -dy   dx ] [ x-x1 ]
    #    [ s ] = DET  [ -dy1  dx1 ] [ y-y1 ]
    #
    # where DET = (-dx1 * dy + dy1 * dx)
    #
    # if DET is too small, they're parallel
    #
    DET = (-dx1 * dy + dy1 * dx)

    if math.fabs(DET) < DET_TOLERANCE: return (0 ,0 ,0 ,0 ,0)

    # now, the determinant should be OK
    DETinv = 1.0 /DET

    # find the scalar amount along the "self" segment
    r = DETinv * (-dy  * ( x -x1) +  dx * ( y -y1))

    # find the scalar amount along the input line
    s = DETinv * (-dy1 * ( x -x1) + dx1 * ( y -y1))

    # return the average of the two descriptions
    xi = (x1 + r* dx1 + x + s * dx) / 2.0
    yi = (y1 + r * dy1 + y + s * dy) / 2.0
    return (xi, yi, 1, r, s)

def _test_find_line_segment_intersection_1():

    pt1 = (10, 10)
    pt2 = (20, 20)

    pt3 = (10, 20)
    pt4 = (20, 10)

    result = find_line_segment_intersection_1(pt1, pt2, pt3, pt4)
    print(result)

def find_line_segment_intersection_2(p0, p1, p2, p3) :

    s10_x = p1[0] - p0[0]
    s10_y = p1[1] - p0[1]
    s32_x = p3[0] - p2[0]
    s32_y = p3[1] - p2[1]

    denom = s10_x * s32_y - s32_x * s10_y

    if denom == 0 : return None # collinear

    denom_is_positive = denom > 0

    s02_x = p0[0] - p2[0]
    s02_y = p0[1] - p2[1]

    s_numer = s10_x * s02_y - s10_y * s02_x

    if (s_numer < 0) == denom_is_positive : return None # no collision

    t_numer = s32_x * s02_y - s32_y * s02_x

    if (t_numer < 0) == denom_is_positive : return None # no collision

    if (s_numer > denom) == denom_is_positive or (t_numer > denom) == denom_is_positive : return None # no collision

    # collision detected

    t = t_numer / denom

    intersection_point = [ p0[0] + (t * s10_x), p0[1] + (t * s10_y) ]

    return intersection_point

def _test_find_line_segment_intersection_2():

    pt1 = (10, 10)
    pt2 = (20, 20)

    pt3 = (10, 20)
    pt4 = (20, 10)

    result = find_line_segment_intersection_2(pt1, pt2, pt3, pt4)
    print(result)

def points_in_ploy(points_xy: list, poly_xy: list):

    poly_xy_list = list(poly_xy)
    poly_xy_list.append(poly_xy_list[0])

    results = np.full((len(points_xy),), False, dtype=np.bool)

    for i, point in enumerate(points_xy):
        p1 = (-1e100, point[1])
        p2 = (1e100, point[1])

        count_intersection = 0
        for j in range(len(poly_xy_list) - 1):
            p3 = poly_xy_list[j]
            p4 = poly_xy_list[j + 1]

            if find_line_segment_intersection_2(p1, p2, p3, p4):
                count_intersection += 1

        if count_intersection == 2:
            results[i] = True

    return results

def _test_points_in_ploy():
    points_xy = (
        (0, 0),
        (1, 1),
        (2, 2),
        (3, 3),
        (4, 4),
        (5, 5)
    )

    poly_xy = [
        (1.1, 1.1),
        (3.9, 1.1),
        (3.9, 3.9),
        (1.1, 3.9)
    ]

    res = points_in_ploy(points_xy, poly_xy)

    print(res)

def ray_tracing_numpy(x, y, poly):

    n = len(poly)
    inside = np.zeros(len(x),np.bool_)
    p2x = 0.0
    p2y = 0.0
    xints = 0.0
    p1x, p1y = poly[0]
    for i in range(n+1):
        p2x, p2y = poly[i % n]
        idx = np.nonzero((y > min(p1y, p2y)) & (y <= max(p1y, p2y)) & (x <= max(p1x,p2x)))[0]
        if p1y != p2y:
            xints = (y[idx]-p1y)*(p2x-p1x)/(p2y-p1y)+p1x
        if p1x == p2x:
            inside[idx] = ~inside[idx]
        else:
            idxx = idx[x[idx] <= xints]
            inside[idxx] = ~inside[idxx]

        p1x,p1y = p2x,p2y
    return inside

if __name__ == "__main__":
    # _test_find_line_segment_intersection_1()
    # _test_find_line_segment_intersection_2()
    _test_points_in_ploy()
    _test_rotation_meshgrid()
    _test_angle_between_two_vectors()


from __future__ import division

import sys
import math
import numpy as np

def calculate_entropy(y):
    'Return the entroy of label y'
    log2 = lambda x: math.log(x) / math.log(2)

    entropy = 0
    unique_labels = np.unique(y)
    for label in unique_labels:
        count = len(y[y == label])
        p = count / len(y)
        entropy += -p * log2(p)

    return entropy

def mean_squared_error(y_true, y_pred):
    'Return mean squared error between prediction and true value'
    mse = np.mean(np.power(y_true - y_pred, 2))
    return mse

def calculate_variance(X):
    'Return variance of the features X'
    mean = np.ones(np.shape(X)) * X.mean(0)
    n_samples = np.shape(X)[0]
    variance = (1 / n_samples) * np.diag((X - mean).T.dot(X - mean))
    return variance

def calculate_std_dev(X):
    'Return standard_deviation of features X'
    std_dev = np.sqrt(calculate_variance(X))
    return std_dev

def euclidean_distance(v1, v2):
    'Return l2 distance between 2 vectors'
    distance = 0
    for i in range(len(v1)):
        distance += pow((v1[i] - v2[i]), 2)
    return math.sqrt(distance)

def accuracy_score(y_true, y_pred):
    'Return accuracy after comparing prediction and true value'
    accuracy = np.sum(y_true == y_pred, axis=0) / len(y_true)
    return accuracy

def calculate_covariance_matrix(X, M=None):
    'Return covariance matrix for dataset X'
    if M is None: M = X
    n_samples = np.shape(X)[0]
    covariance_matrix = (1 / (n_samples - 1)) * (X - X.mean(axis=0)).T.dot(M - M.mean(axis=0))
    return np.array(covariance_matrix, dtype = float)

def calculate_correlation_matrix(X, M=None):
    'Return correlation matrix for dataset X'
    if M is None: M = X

    covariance = calculate_covariance_matrix(X)
    X_std_dev = np.expand_dims(calculate_std_dev(X), 1)
    y_std_dev = np.expand_dims(calculate_std_dev(M), 1)

    correlation_matrix = np.divide(covariance, X_std_dev.dot(y_std_dev.T))
    return np.array(correlation_matrix, dtype=float)


import numba
import numpy as np

def proof_of_pi(no_of_sims):
    """
    Proof pi using random numbers
    Given a circle radius = 1
    calculate how many random points fall between 1 distance from the centre.
    multiply by 4 as we only get quarter of a circle
    """
    sum = 0
    for i in range(no_of_sims):
        x = np.random.uniform()
        y = np.random.uniform()
        if ((x ** 2) + (y ** 2)) <= 1:
            sum += 1/no_of_sims
    return sum * 4
    # return 4 * sum([(np.random.uniform() ** 2 + np.random.uniform() ** 2 <= 1) for _ in range(no_of_sims)]) / no_of_sims

def proof_of_euler(big_number):
    """
    proof euler number using exponential growth
    """
    return (1 + 1/big_number) ** big_number

if __name__ == '__main__':
    no_paths = 100000
    delegate_proof_of_pi = numba.jit(proof_of_pi)

    print('numerical pi:', delegate_proof_of_pi(no_paths))
    print('real pi:', np.pi)
    print('numerical euler:', proof_of_euler(no_paths))
    print('euler', np.exp(1))


"""
A bag contains one red disc and one blue disc. In a game of chance a player
takes a disc at random and its colour is noted. After each turn the disc is
returned to the bag, an extra red disc is added, and another disc is taken at
random.

The player pays £1 to play and wins if they have taken more blue discs than red
discs at the end of the game.

If the game is played for four turns, the probability of a player winning is
exactly 11/120, and so the maximum prize fund the banker should allocate for
winning in this game would be £10 before they would expect to incur a loss.
Note that any payout will be a whole number of pounds and also includes the
original £1 paid to play the game, so in the example given the player actually
wins £9.

Find the maximum prize fund that should be allocated to a single game in which
fifteen turns are played.

Solution comment: Very quick, ~0.5 ms (would be much less for C++ impl.).
Derived explicitly the N = 4 case above, from which the pattern emerges.  The
memoization helps a whole bunch. Would have been slightly better to use DP and
build the table directly, but memoizing a recursive solution is so much nicer.
"""

import time
import numpy as np
from functools import lru_cache

def P(n):
    """Probability of drawing a blue in the n-th round."""
    return 1 / (n + 1)

@lru_cache(maxsize=None)
def Prob(blues, n, N):
    """
    Return probability of getting some number of blues, starting on round n,
    with N rounds total.
    """
    if blues <= 0:
        return 1    # Can always get 0 blues.
    elif blues > N - n + 1:
        return 0    # Can never get more blues than draws.
    elif blues == N - n + 1:
        return np.prod(P(np.arange(n, N+1)))  # Prob of getting blue of every draw.
    else:
        # Prob is prob of getting a blue now and then blues-1 on the remaining throws,
        # or prob of getting red now and blues blues on the remaining throws.
        return P(n) * Prob(blues - 1, n + 1, N) + (1-P(n)) * Prob(blues, n + 1, N)

if __name__ == "__main__":
    t0 = time.time()
    rounds = 15
    bet = 1
    winning_prob = Prob(rounds // 2 + 1, 1, rounds)
    print('Execution time: {:.3f} ms'.format((time.time() - t0) * 1e3))
    print('Max payout with {} rounds: {}£'.format(rounds, int(bet / winning_prob)))


# coding: utf-8

# In[3]:

'''
Statistical Computing for Scientists and Engineers
Homework 4 Problem 3 a
Fall 2018
University of Notre Dame
'''
import numpy as np
import matplotlib.pyplot as plt
import math
import scipy.stats

# the true distribution
def f(v):
    return scipy.stats.gamma.pdf(v,a=4.3,scale=1/6.2)
# the proposal distribution
def q(v):
    return scipy.stats.gamma.pdf(v,a=4,scale=1/7)

# supremum of f/q
M = 2.0

#Initialization
numSamples = 50000

x= np.linspace(0,4,1001)

#Accept - Reject algorithm, Sample from laplacian
def accept_reject():
    samples = np.random.gamma(4,1/7,numSamples)
    acceptanceProb = f(samples)/(M*q(samples))
    unif_samp = np.random.rand(1,numSamples)
    accepted = unif_samp < acceptanceProb
    return samples, accepted, unif_samp

# all the samps, accepted, unif_samps
samps,accepteds,unif_samps = accept_reject()

#filter the accepted samps
Samp= np.array([])
Unif_samp = np.array([])
SumN = 0
for i in range(0,numSamples):
    if (accepteds[0][i]==1):
        Samp=np.append(Samp,[samps[i]])
        Unif_samp = np.append(Unif_samp,[unif_samps[0][i]])
        SumN=SumN+1 # SumN counts the accepted samps

#calculate the expectation
E = np.array([0.0]*SumN)
Sum = 0
List = np.array([0]*SumN)
for i in range(0,SumN):
        Sum= Sum+Samp[i];
        E[i]=Sum/(i+1);
        List[i]=i+1

#plot the expectation
plt.figure(figsize=(8,8))
plt.plot(List,E)
plt.ylabel("<E>")
plt.xlabel("Iteration")
plt.savefig('h4p3a1.png')
plt.show()

#calculate the convergence
cov = np.array([0.0]*SumN)
for i in range(0,SumN):
    cov[i]=np.mean(pow(E[0:i]-Samp[0:i],2))

#plot the covergence
plt.figure(figsize=(8,8))
plt.plot(List,cov)
plt.ylabel("COV")
plt.xlabel("Iteration")
plt.savefig('h4p3a2.png')
plt.show()

#plot the True distribution & proposal distribution & accepted samps
plt.figure(figsize=(8,8))
plt.plot(x,M*q(x),c='orange',label=u'Proposal distribution')
plt.plot(x, f(x), 'r', label=u'True distribution') # f(x) is the True distribution
plt.scatter(Samp,M*Unif_samp*q(Samp),s=2,c='blue')
plt.legend()
plt.xlim([0,4])
plt.savefig('h4p3a3.png')
plt.show()

# plot histogram & true distribution
plt.figure(figsize=(8,8))
plt.hist(Samp,bins=50, alpha=0.4, label=u'sampled histogram', normed=True)
plt.plot(x, f(x), 'r', label=u'True distribution') # f(x) is the True distribution
plt.legend()
plt.xlim([0,4])
plt.savefig('h4p3a4.png')
plt.show()


from sympy import Symbol, exp, log
from sympy.calculus.singularities import singularities

from sympy.utilities.pytest import XFAIL

def test_singularities():
    x = Symbol('x', real=True)

    assert singularities(x**2, x) == ()
    assert singularities(x/(x**2 + 3*x + 2), x) == (-2, -1)

@XFAIL
def test_singularities_non_rational():
    x = Symbol('x', real=True)

    assert singularities(exp(1/x), x) == (0)
    assert singularities(log((x - 2)**2), x) == (2)


# -*- coding: utf-8 -*-
"""
Created on Wed Apr 10 20:46:58 2019

@author:    Manuel Martinez
@project:   Mathematics for Machine Learning.
@purpose:   Week 2 PCA programming assignment
"""

# PACKAGE: DO NOT EDIT THIS LINE
import matplotlib as mpl
import matplotlib.pyplot as plt
import numpy as np
import scipy

import sklearn
# from ipywidgets import interact
# from load_data import load_mnist

def distance(x0, x1):
    """Compute distance between two vectors x0, x1 using the dot product"""
    d_vec = x0 - x1
    distance = np.sqrt(d_vec @ d_vec)
    return distance

def angle(x0, x1):
    """Compute the angle between two vectors x0, x1 using the dot product"""
    dot = x0 @ x1
    l1 = np.sqrt(x0 @ x0)
    l2 = np.sqrt(x1 @ x1)
    angle = np.arccos(dot / (l1 * l2))
    return angle

# find most similar
# np.argmin([distance(images[0], images[i]) for i in range(1, len(images))]) + 1

# GRADED FUNCTION: DO NOT EDIT THIS LINE
def most_similar_image():
    """Find the index of the digit, among all MNIST digits
       that is the second-closest to the first image in the dataset
       (the first image is closest to itself trivially).
       Your answer should be a single integer.
    """
    index = 61
    return index

# GRADED FUNCTION: DO NOT EDIT THIS LINE
def pairwise_distance_matrix(X, Y):
    """Compute the pairwise distance between rows of X and rows of Y

    Arguments
    ----------
    X: ndarray of size (N, D)
    Y: ndarray of size (M, D)

    Returns
    --------
    distance_matrix: matrix of shape (N, M), each entry distance_matrix[i,j]
    is the distance between ith row of X and the jth row of Y
    (we use the dot product to compute the distance).
    """
    if len(Y.shape) == 1:
        Y = Y.reshape(1, -1)
    N, D = X.shape
    M, D = Y.shape
    xydiff = X[:, :, None] - Y[:, :, None].T
    distance_matrix = np.sqrt((xydiff * xydiff).sum(1))
    return distance_matrix

# GRADED FUNCTION: DO NOT EDIT THIS LINE
def KNN(k, X, y, x):
    """K nearest neighbors
    k: number of nearest neighbors
    X: training input locations
    y: training labels
    x: test input
    """
    if len(x.shape) == 1:
        x = x.reshape(1, -1)
    N, D = X.shape
    num_classes = len(np.unique(y))
    dist = pairwise_distance_matrix(X, x)
    if dist.shape[1] == 1:
        dist = dist.ravel()

    # Next we make the predictions
    ypred = np.zeros(num_classes)
    # find the labels of the k nearest neighbors
    classes = y[np.argsort(dist)][:k]
    for c in classes:
        ypred[c] += 1

    return np.argmax(ypred)


import numpy as np

from sdia_python.lab2.utils import get_random_number_generator

class BallWindow:
    """Represents a ball in any dimension, defined by a center and a radius"""

    def __init__(self, center, radius):
        """Initializes a ball with a center and a radius. The radius must be positive.

        Args:
            center (numpy.array): coordinates of the center point
            radius (float): float representing the radius
        """
        center = np.array(center)
        assert len(center)
        assert radius >= 0

        self.center = center
        self.radius = float(radius)

    def __str__(self):
        return f"BallWindow: ({str(self.center)}, {str(self.radius)})"

    def __len__(self):
        """Returns the dimension of the ball

        Returns:
            int : dimension of the ball
        """
        return len(self.center)

    def __contains__(self, point):
        """Tells whether a point is contained in the ball

        Args:
            point (numpy.array): the point to test

        Returns:
            boolean: if it is contained in the ball
        """
        point = np.array(point)
        assert self.dimension() == point.size

        return np.linalg.norm(point - self.center) <= self.radius

    def dimension(self):
        """Returns the dimension of the ball

        Returns:
            int : dimension of the ball
        """
        return len(self)

    def volume(self):
        """Returns the volume of the ball. The formula for the volume of an n-ball is used : https://fr.wikipedia.org/wiki/N-sph%C3%A8re

        Returns:
            float : volume of the ball
        """
        n = self.dimension()
        R = self.radius
        if n % 2 == 0:  # formula in case dimension is even
            return (((np.pi) ** (n / 2)) * R ** n) / np.math.factorial(n / 2)
        else:  # formula in case dimension is odd
            odds = [i for i in range(1, n + 1, 2)]
            product = np.product(odds)
            return 2 ** ((n + 1) / 2) * np.pi ** ((n - 1) / 2) * R ** n / product

    def indicator_function(self, points):
        """Returns true if all points are in the ball.

        Args:
            points (np.array): Array of points to test

        Returns:
            bool: True if all points are in the box.
        """
        return np.all([point in self for point in points])

    def rand(self, n=1, rng=None):
        """Generates n points in the ball.

        Args:
            n (int, optional): Number of points generated. Defaults to 1.
            rng (int, optional): Random seed. Defaults to None.

        Returns:
            numpy.array: array containing the n generated points
        """
        rng = get_random_number_generator(rng)

        directions = rng.uniform(size=(n, self.dimension()))
        directions = np.array(
            [direction / np.linalg.norm(direction) for direction in directions]
        )
        distances = rng.uniform(0, self.radius, n)
        vectors = np.array(
            [
                direction * distance
                for (direction, distance) in zip(directions, distances)
            ]
        )
        return np.array([vector + self.center for vector in vectors])


"""
Author : Achintya Gupta
Date Created : 22-09-2020
"""

"""
Problem Statement
------------------------------------------------
Q) Surprisingly there are only three numbers that can be written as the sum of fourth powers of their digits:

                1634 = 14 + 64 + 34 + 44
                8208 = 84 + 24 + 04 + 84
                9474 = 94 + 44 + 74 + 44
                As 1 = 14 is not a sum it is not included.

    The sum of these numbers is 1634 + 8208 + 9474 = 19316.
    Find the sum of all the numbers that can be written as the sum of fifth powers of their digits.
"""
from utils import timing_decorator
import numpy as np

@timing_decorator
def digit_n_power(power=5):
    # Figure out the upper limit
    llimit,ulimit=0,int(1e9)
    for i in range(1,100):
        maxpossible = i*(9**power)
        if i>=len(str(maxpossible)):
            ulimit = int("".join(['9']*i))
            print(llimit, ulimit)
            break
    nums = []
    for i in range(llimit, ulimit):
        if sum([int(k)**power for k in list(str(i))]) == i and i not in [0,1]:
            nums.append(i)

    print(f'Sum : ', sum(nums))

digit_n_power()


import numpy as np
import matplotlib.pyplot as plt

# this calculates the derivatives
def b_j(x_list, y_list, i):
    if i == 0:
        b1 = (y_list[i+1] - y_list[i])/(x_list[i+1] - x_list[i])
        b2 = (y_list[i+2] - y_list[i])/(x_list[i+2] - x_list[i])
    elif i == len(x_list) - 1:
        b1 = (y_list[i] - y_list[i-1])/(x_list[i] - x_list[i-1])
        b2 = (y_list[i + 1] - y_list[i]) / (x_list[i + 1] - x_list[i])
    else:
        b1 = (y_list[i+1] - y_list[i-1]) / (x_list[i+1] - x_list[i-1])
        b2 = (y_list[i+2] - y_list[i]) / (x_list[i+2] - x_list[i])
    return b1, b2

# calculating the v_i terms
def v(x_i, x0, x1):
    v1 = (x_i-x0)*((x1-x_i)/(x1-x0))**2
    v2 = (x_i-x1)*((x0-x_i)/(x0-x1))**2
    return v1, v2

# calculating the u_i terms
def u(x_i, x0, x1):
    u1 = (1-2*(x_i-x0)/(x0-x1))*((x_i-x1)/(x0-x1))**2  # changed (x0-x1) to (x1-x0)
    u2 = (1-2*(x1-x_i)/(x1-x0))*((x0-x_i)/(x0-x1))**2  # original
    return u1, u2

def cubic_hermite(x_i, x, y):  # x_i is a point, x is a list of points, y is a list of points
    # we are doing the interpolation for 6 points, therefore there are 5 sub-domains
    # make an array of the different coefficients
    a_arr = []
    b_arr = []

    for i in range(6):
        # add y values
        a_arr.append(y[i])
        # add b values
        if i == 0:
            b_arr.append((y[i+1] - y[i])/(x[i+1] - x[i]))
        elif i == len(x) - 1:  # big guy did smthg different
            b_arr.append((y[i] - y[i - 1]) / (x[i] - x[i - 1]))
        else:
            b_arr.append((y[i+1] - y[i-1]) / (x[i+1] - x[i-1]))
    k = 0
    # calculate where the value lies w.r.t the other arrays
    while x_i > x[k]:
        k += 1

    # Now, calculate the value of the function at that point
    u1 = (1 - 2 * (x_i - x[k-1]) / (x[k-1] - x[k])) * ((x_i - x[k]) / (x[k-1] - x[k])) ** 2
    u2 = (1 - 2 * (x_i - x[k]) / (x[k] - x[k-1])) * ((x_i - x[k-1]) / (x[k] - x[k-1])) ** 2

    v1 = (x_i - x[k-1]) * ((x_i - x[k]) / (x[k-1] - x[k])) ** 2
    v2 = (x_i - x[k]) * ((x[k-1] - x_i) / (x[k] - x[k-1])) ** 2

    val = (a_arr[k-1] * u1 + b_arr[k-1]*v1 + a_arr[k]*u2 + b_arr[k] * v2)
    return val

if __name__ == "__main__":
    B = [0, 0.2, 0.4, 0.6, 0.8, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9]
    H = [0, 14.7, 36.5, 71.7, 121.4, 197.4, 256.2, 348.7, 540.6, 1062.8, 2318.0, 4781.9, 8687.4, 13924.3, 22650.2]

    B_2 = [0, 1.3, 1.4, 1.7, 1.8, 1.9]
    H_2 = [0, 540.6, 1062.8, 8687.4, 13924.3, 22650.2]

    x = np.arange(0, 1.9, 0.1)
    y_extrap = []

    for l in range(len(x)):
        y_extrap.append(cubic_hermite(x[l], B_2, H_2))

    # plot the extrapolation for Q1c)
    plt.plot(x, y_extrap, label='Cubic Hermite')
    plt.plot(B, H, label='H(B) curve')
    plt.xlabel("B field (T)")
    plt.ylabel("H field (A/M)")
    plt.legend(loc="upper left")
    plt.show()


from typing import List

import numpy as np

def arithmetic_mean(values: List[float]) -> float:
    """
    Computes the arithmetic mean of a list of values.
    """
    return sum(values) / len(values)

def geometric_mean(values: List[float]) -> float:
    """
    Computes the geometric mean of a list of values.
    """
    a = np.array(values)
    return a.prod() ** (1.0 / len(a))


"""
This script generates the plots to provide graphs helping to solve exercise 3.4.2.

The differential equation is given by:

x' = r*x - sinh(x)

An obvious fixed point is x* = 0 and the remaining piece of the bifurcation diagram
can be drawn by inverting the plot of r(x) = sinh(x) / x, albeit without the stability
information.
"""
import matplotlib.pyplot as plt
import numpy as np

def r(x):
    """
    Express r in terms of the fixed point x*.

    This is done to obtain the bifurcation diagram for x* != 0.
    """
    return np.sinh(x) / x

if __name__ == "__main__":
    # Plot r as a function of the fixed points
    # Inverting the axes of this plot, we obtain the bifurcation
    # diagram, albeit without the stability information (although,
    # if we know the general appearance of sub and supercritcal
    # pitchfork bifurcations, we can infer the stability of the
    # fixed points from the information of whether they appear
    # to the right of the bifurcation (stable) or to the left (unstable)).
    x_vals = np.linspace(-1, 1, 1000)
    r_vals = r(x_vals)

    plt.plot(x_vals, r_vals)
    plt.title(r"$r$ as a function of the fixed points $x^*$")
    plt.xlabel(r"$x^*$")
    plt.ylabel(r"$r(x^*)$")
    plt.show()

    # Compare rx and sinh(x) to determine the fixed points (from their intersection)
    # as well as their stability.

    x_vals = np.linspace(-3, 3, 1000)

    def rx(x, r):
        return x*r

    plt.plot(x_vals, np.sinh(x_vals), label=r"$\sinh(x)$")
    plt.plot(x_vals, rx(x_vals, -1), label=r"r=-1")
    plt.plot(x_vals, rx(x_vals, 0), label=r"r=0")
    plt.plot(x_vals, rx(x_vals, 1), label=r"r=1")
    plt.plot(x_vals, rx(x_vals, 2), label=r"r=2")
    plt.title(r"$rx$ vs $\sinh(x)$")
    plt.xlabel("$x$")
    plt.ylabel("$f(x)$")
    plt.legend()
    plt.show()


#
# Valuation of European Call Options in BSM Model
# and Numerical Derivation of Implied Volatility
# 03_stf/BSM_imp_vol.py
#
# (c) Dr. Yves J. Hilpisch
# from Hilpisch, Yves (2014): Python for Finance, O'Reilly.
#
from math import log, sqrt, exp
from scipy import stats
from scipy.optimize import fsolve

class call_option(object):
    ''' Class for European call options in BSM Model.

    Attributes
    ==========
    S0 : float
        initial stock/index level
    K : float
        strike price
    t : datetime/Timestamp object
        pricing date
    M : datetime/Timestamp object
        maturity date
    r : float
        constant risk-free short rate
    sigma : float
        volatility factor in diffusion term

    Methods
    =======
    value : float
        return present value of call option
    vega : float
        return vega of call option
    imp_vol : float
        return implied volatility given option quote
    '''

    def __init__(self, S0, K, t, M, r, sigma):
        self.S0 = float(S0)
        self.K = K
        self.t = t
        self.M = M
        self.r = r
        self.sigma = sigma

    def update_ttm(self):
        ''' Updates time-to-maturity self.T. '''
        if self.t > self.M:
            raise ValueError("Pricing date later than maturity.")
        self.T = (self.M - self.t).days / 365.

    def d1(self):
        ''' Helper function. '''
        d1 = ((log(self.S0 / self.K)
            + (self.r + 0.5 * self.sigma ** 2) * self.T)
            / (self.sigma * sqrt(self.T)))
        return d1

    def value(self):
        ''' Return option value. '''
        self.update_ttm()
        d1 = self.d1()
        d2 = ((log(self.S0 / self.K)
            + (self.r - 0.5 * self.sigma ** 2) * self.T)
            / (self.sigma * sqrt(self.T)))
        value = (self.S0 * stats.norm.cdf(d1, 0.0, 1.0)
            - self.K * exp(-self.r * self.T) * stats.norm.cdf(d2, 0.0, 1.0))
        return value

    def vega(self):
        ''' Return Vega of option. '''
        self.update_ttm()
        d1 = self.d1()
        vega = self.S0 * stats.norm.pdf(d1, 0.0, 1.0) * sqrt(self.T)
        return vega

    def imp_vol(self, C0, sigma_est=0.2):
        ''' Return implied volatility given option price. '''
        option = call_option(self.S0, self.K, self.t, self.M,
                             self.r, sigma_est)
        option.update_ttm()
        def difference(sigma):
            option.sigma = sigma
            return option.value() - C0
        iv = fsolve(difference, sigma_est)[0]
        return iv


from scipy.optimize import fmin

def shortest_confidence_interval(dist_name, confidence_coef=0.95, **args):
    """ Cálculo del intervalo de confianza mas corto.

    :param dist_name: nombre scipy de la distribución (str)
    :param confidence_coef: coeficiente de confianza (float entre 0 y 1)
    :param args: argumentos scipy de la distribución
    :return: límite inferior y superior del intervalo (array [low, high])

    Ejemplo de uso:

    c1, c2 = shortest_confidence_interval(beta, confidence_coef=0.9, a=5, b=3)
    """
    # configuración de la distribución con los argumentos dados
    distri = dist_name(**args)
    # estimación inicial de la probabilidad (área) de la cola inferior
    low_tail_pr_ini = 1.0 - confidence_coef

    def interval_width(low_tail_pr):
        """
        Cálculo del intervalo de confianza cuando el área de la cola inferior es
        low_tail_pr, por lo que el límite inferior es F^{-1}(low_tail_pr).

        :param low_tail_pr: probabilidad de la cola inferior (float entre 0 y 1)
        :return: largo del intervalo (float)
        """
        return distri.ppf(confidence_coef + low_tail_pr) - distri.ppf(low_tail_pr)

    # Busqueda de la probabilidad de la cola inferior (low_tail_pr) que minimiza el
    # intervalo de confianza (función interval_width) fijando el coeficiente de confianza.
    low_tail_pr_short = fmin(interval_width, low_tail_pr_ini, ftol=1e-8, disp=False)[0]
    # Se retorna el intervalo como un array([low, high])
    return distri.ppf([low_tail_pr_short, confidence_coef + low_tail_pr_short])


from sympy import *
from rodrigues_R_utils import *

x_1, y_1, z_1 = symbols('x_1 y_1 z_1')
px_1, py_1, pz_1 = symbols('px_1 py_1 pz_1')
sx_1, sy_1, sz_1 = symbols('sx_1 sy_1 sz_1')
x_2, y_2, z_2 = symbols('x_2 y_2 z_2')
px_2, py_2, pz_2 = symbols('px_2 py_2 pz_2')
sx_2, sy_2, sz_2 = symbols('sx_2 sy_2 sz_2')

position_symbols_1 = [px_1, py_1, pz_1]
rodrigues_symbols_1 = [sx_1, sy_1, sz_1]
position_symbols_2 = [px_2, py_2, pz_2]
rodrigues_symbols_2 = [sx_2, sy_2, sz_2]
all_symbols = position_symbols_1 + rodrigues_symbols_1 + position_symbols_2 + rodrigues_symbols_2

point_1 = Matrix([x_1, y_1, z_1, 1]).vec()
point_2 = Matrix([x_2, y_2, z_2, 1]).vec()

transformed_point_1 = (matrix44FromRodrigues(px_1, py_1, pz_1, sx_1, sy_1, sz_1) * point_1)[:-1,:]
transformed_point_2 = (matrix44FromRodrigues(px_2, py_2, pz_2, sx_2, sy_2, sz_2) * point_2)[:-1,:]

delta=Matrix([0,0,0]).vec()-(transformed_point_1-transformed_point_2)
delta_jacobian=delta.jacobian(all_symbols)

print(delta)
print(delta_jacobian)

with open("point_to_point_rodrigues_wc_jacobian.h",'w') as f_cpp:
    f_cpp.write("inline void point_to_point_rodrigues_wc(double &delta_x, double &delta_y, double &delta_z, double px_1, double py_1, double pz_1, double sx_1, double sy_1, double sz_1, double px_2, double py_2, double pz_2, double sx_2, double sy_2, double sz_2, double x_1, double y_1, double z_1, double x_2, double y_2, double z_2)\n")
    f_cpp.write("{")
    f_cpp.write("delta_x = %s;\n"%(ccode(delta[0,0])))
    f_cpp.write("delta_y = %s;\n"%(ccode(delta[1,0])))
    f_cpp.write("delta_z = %s;\n"%(ccode(delta[2,0])))
    f_cpp.write("}")
    f_cpp.write("\n")
    f_cpp.write("inline void point_to_point_rodrigues_wc_jacobian(Eigen::Matrix<double, 3, 12, Eigen::RowMajor> &j, double px_1, double py_1, double pz_1, double sx_1, double sy_1, double sz_1, double px_2, double py_2, double pz_2, double sx_2, double sy_2, double sz_2, double x_1, double y_1, double z_1, double x_2, double y_2, double z_2)\n")
    f_cpp.write("{")
    for i in range (3):
        for j in range (12):
            f_cpp.write("j.coeffRef(%d,%d) = %s;\n"%(i,j, ccode(delta_jacobian[i,j])))
    f_cpp.write("}")


import numpy as np
from scipy.special import eval_hermite, factorial

def hermite_functions(n, x, all_n=True, move_axes=(), method="recursive"):
    """
    Calculate the Hermite functions up to the nth order at position x, psi_n(x).

    For details see:
    https://en.wikipedia.org/wiki/Hermite_polynomials#Hermite_functions

    If all_n == True, then return all Hermite functions up to n
    If all_n == False, only return nth Hermite function
    If using recursive method, then the latter is more memory efficient as it
    only stores psi_n, psi_{n-1}, and psi_{n-2}

    The 'move_axes' option causes the output dimensions to be swapped around
    using np.moveaxis.

    Uses one of three possible calculation methods:
        'recursive' - Uses recursive method. Most efficient for n > 5.
        'direct'    - Calculates directly using Hermite polynomials.
                      Inefficient due to factorial and Hermite polynomial,
                      although useful for comparison when testing
        'analytic'  - Uses analytic expressions (only for n <= 5)

    Recursion relation:
        psi_n(x) = sqrt(2/n) * x * psi_{n-1}(x) - sqrt((n-1)/n) * psi_{n-2}(x)

    Examples:

    >>> x = np.mgrid[-2:3, 0:4]
    >>> x.shape
    (2, 5, 4)
    >>> n = 5
    >>> psi = hermite_functions(n, x, all_n=False)
    >>> psi.shape
    (2, 5, 4)
    >>> psi = hermite_functions(n, x, all_n=True)
    >>> psi.shape
    (6, 2, 5, 4)
    >>> reshape = ([0, 1, 2, 3], [1, 3, 2, 0])
    >>> psi = hermite_functions(n, x, all_n=True, move_axes=reshape)
    >>> psi.shape
    (4, 6, 5, 2)

    """

    if method not in ["recursive", "analytic", "direct"]:
        raise ValueError("Method not recognized.")
    if not (issubclass(type(n), int) or issubclass(type(n), np.integer)):
        raise TypeError("n must be an integer.")
    if n < 0:
        raise ValueError("n must be non-negative.")
    if method == "analytic" and (n > 5):
        raise ValueError("n must not be greater than 5 for analytic calculation.")

    if all_n:
        psi_n = _Hermite_all_n(n, x, method)
    else:
        psi_n = _Hermite_single_n(n, x, method)

    if move_axes:
        psi_n = np.moveaxis(psi_n, move_axes[0], move_axes[1])

    return psi_n

def _Hermite_single_n(n, x, method):
    """
    Calculates psi_n(x) for a single value of n.
    """

    if method == "analytic":
        return _H_analytic(n, x)

    if method == "direct":
        return _H_direct(n, x)

    psi_m_minus_2 = _H_analytic(0, x)
    if n == 0:
        return psi_m_minus_2

    psi_m_minus_1 = _H_analytic(1, x)
    if n == 1:
        return psi_m_minus_1

    for m in range(2, n + 1):
        psi_m = _H_recursive(m, x, psi_m_minus_2, psi_m_minus_1)
        psi_m_minus_2 = psi_m_minus_1
        psi_m_minus_1 = psi_m

    return psi_m

def _Hermite_all_n(n, x, method):
    """
    Calcualtes psi_m(x) for all 0 <= m <= n.
    """

    try:
        psi_n = np.zeros((n + 1,) + x.shape)
    except AttributeError:  # x does not have property 'shape'
        psi_n = np.zeros((n + 1, 1))

    if method == "analytic":
        for m in range(n + 1):
            psi_n[m, :] = _H_analytic(m, x)
        return psi_n

    if method == "direct":
        for m in range(n + 1):
            psi_n[m, :] = _H_direct(m, x)
        return psi_n

    psi_n[0, :] = _H_analytic(0, x)
    if n == 0:
        return psi_n

    psi_n[1, :] = _H_analytic(1, x)
    if n == 1:
        return psi_n

    for m in range(2, n + 1):
        psi_n[m, :] = _H_recursive(m, x, psi_n[m - 2, :], psi_n[m - 1, :])

    return psi_n

def _H_recursive(m, x, psi_m_minus_2, psi_m_minus_1):
    """
    Calculate psi_m(x) using recursion relation.
    """
    return np.sqrt(2 / m) * x * psi_m_minus_1 - np.sqrt((m - 1) / m) * psi_m_minus_2

def _H_analytic(n, x):
    """
    Analytic expressions for psi_n(x) for 0 <= n <= 5.
    """

    if n == 0:
        return np.pi ** (-1 / 4) * np.exp(-(x ** 2) / 2)
    if n == 1:
        return np.sqrt(2) * np.pi ** (-1 / 4) * x * np.exp(-(x ** 2) / 2)
    if n == 2:
        return (
            (np.sqrt(2) * np.pi ** (1 / 4)) ** (-1)
            * (2 * x ** 2 - 1)
            * np.exp(-(x ** 2) / 2)
        )
    if n == 3:
        return (
            (np.sqrt(3) * np.pi ** (1 / 4)) ** (-1)
            * (2 * x ** 3 - 3 * x)
            * np.exp(-(x ** 2) / 2)
        )
    if n == 4:
        return (
            (2 * np.sqrt(6) * np.pi ** (1 / 4)) ** (-1)
            * (4 * x ** 4 - 12 * x ** 2 + 3)
            * np.exp(-(x ** 2) / 2)
        )
    if n == 5:
        return (
            (2 * np.sqrt(15) * np.pi ** (1 / 4)) ** (-1)
            * (4 * x ** 5 - 20 * x ** 3 + 15 * x)
            * np.exp(-(x ** 2) / 2)
        )
    raise ValueError("n must be an integer between 0 and 5")

def _H_direct(n, x):
    """
    Calculate psi_n(x) using explicit definition.
    """
    return (
        1
        / np.sqrt(2 ** n * factorial(n))
        * np.pi ** (-1 / 4)
        * np.exp(-(x ** 2) / 2)
        * eval_hermite(n, x)
    )

if __name__ == "__main__":
    import doctest

    doctest.testmod()


"""
    Author: Kelvin Tsang
    Github: https://github.com/Kelviiiin/DCT_Basis_Image

    Creates the basis images of discrete cosine tranformation.
    https://upload.wikimedia.org/wikipedia/commons/2/23/Dctjpeg.png
"""

import os
from math import cos, pi
import cv2
import numpy as np

class DCT_Basis_Image:
    def __init__(self):
        # size of the square image 8x8
        self.size = 8
        # folder name where the images are saved
        self.folderName = "BasisImage"

    def create(self):
        # create image set
        imageSet = []
        for v in range(0, self.size):
            for u in range(0, self.size):
                basisImg = self.getBasisImage(u, v)
                imageSet.append(basisImg)

        # create folder
        if (not os.path.isdir(self.folderName)):
            os.makedirs(self.folderName)

        # name of the images are numbered consecutively
        for i in range(0, len(imageSet)):
            number = None
            if(i < 10):
                number = "0" + str(i)
            else:
                number = str(i)
            # image directory
            directory = 'BasisImage/' + number + '.png'
            # save images with cv2
            cv2.imwrite(directory, imageSet[i])

        print('Done')

    # discrete cosinus transform
    def dct(self, x, y, u, v, n):
        return cos(((2 * x + 1) * (u * pi)) / (2 * n)) * cos(((2 * y + 1) * (v * pi)) / (2 * n))

    def cosValueToGrayValue(self, val):
        return int((val+1)/2 * 256)

    def getBasisImage(self, u, v):
        # for a given (u,v), make a DCT basis image
        basisImg = np.zeros((self.size, self.size))
        for y in range(0, self.size):
            for x in range(0, self.size):
                basisImg[y, x] = self.cosValueToGrayValue(self.dct(x, y, u, v, self.size))
        return basisImg

basisImage = DCT_Basis_Image()
basisImage.create()


r"""
Solve Poisson equation on (0, 2pi)x(0, 2pi)x(0, 2pi) with periodic bcs

    \nabla^2 u = f,

Use Fourier basis and find u in VxVxV such that

    (v, div(grad(u))) = (v, f)    for all v in VxVxV

where V is the Fourier basis span{exp(1jkx)}_{k=-N/2}^{N/2-1} and
VxVxV is a tensorproductspace.

"""
import os
from sympy import symbols, cos, sin, lambdify
import numpy as np
from shenfun import inner, div, grad, TestFunction, TrialFunction, FunctionSpace, \
    TensorProductSpace, Array, Function, dx, comm

# Use sympy to compute a rhs, given an analytical solution
x, y, z = symbols("x,y,z", real=True)
ue = cos(4*x) + sin(4*y) + sin(6*z)
fe = ue.diff(x, 2) + ue.diff(y, 2) + ue.diff(z, 2)

# Size of discretization
N = (14, 15, 16)

K0 = FunctionSpace(N[0], 'F', dtype='D')
K1 = FunctionSpace(N[1], 'F', dtype='D')
K2 = FunctionSpace(N[2], 'F', dtype='d')
T = TensorProductSpace(comm, (K0, K1, K2))
u = TrialFunction(T)
v = TestFunction(T)

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

# Compute right hand side
f_hat = Function(T)
f_hat = inner(v, fj, output_array=f_hat)

# Solve Poisson equation
A = inner(v, div(grad(u)))
f_hat = A.solve(f_hat)

uq = T.backward(f_hat, fast_transform=True)

uj = Array(T, buffer=ue)
print(np.sqrt(dx((uj-uq)**2)))
assert np.allclose(uj, uq)

# Test eval at point
point = np.array([[0.1, 0.5], [0.5, 0.6], [0.1, 0.2]])
p = T.eval(point, f_hat)
ul = lambdify((x, y, z), ue)
assert np.allclose(p, ul(*point))
p2 = f_hat.eval(point)
assert np.allclose(p2, ul(*point))

if 'pytest' not in os.environ:
    import matplotlib.pyplot as plt
    plt.figure()
    X = T.local_mesh(True) # With broadcasting=True the shape of X is local_shape, even though the number of datapoints are still the same as in 1D
    plt.contourf(X[0][:, :, 0], X[1][:, :, 0], uq[:, :, 0])
    plt.colorbar()

    plt.figure()
    plt.contourf(X[0][:, :, 0], X[1][:, :, 0], uj[:, :, 0])
    plt.colorbar()

    plt.figure()
    plt.contourf(X[0][:, :, 0], X[1][:, :, 0], uq[:, :, 0]-uj[:, :, 0])
    plt.colorbar()
    plt.title('Error')
    plt.show()


import numpy as np

a = np.array([[50,10,30],
              [60,20,40],
              [70,80,90]])
# return simple average of all element from an array
print()
print(np.average(a))

# For calculating weighted average of all column element from an array
# multiplying each value from each column by its weight,
# then you add up the products and divide this products sum
# by the sum of all weights.

w=[1,2,3]

print()
print(np.average(a,weights=w,axis=0))

# For calculating weighted average of all row element from an array
# multiplying each value from each row by its weight,
# then you add up the products and divide this products sum
# by the sum of all weights.

print()
print(np.average(a,weights=w,axis=1))
print()


# Solving Ax = b using steepest descent method
# Equation: Equation 1: x - y = -10
#           Equation 2: 2x + 3y = 15
# A = [1,-1; 2,3] and b = [-10; 15]

import numpy as np

A = np.zeros((2,2))         # Define A matrix
A[0,0] = 1.0
A[0,1] = -1.0
A[1,0] = 2.0
A[1,1] = 3.0

b = np.zeros((2,1))         # Define b vector
b[0] = -10.0
b[1] = 15.0

res = np.ones((2,1))        # Define the residue res = b - Ax
res[0] = 10
res[1] = 10

x = np.ones((2,1))          # Initial guess solution
x[0] = 0
x[1] = 0

count = 0
while res[0] > 0.001 or res[1] > 0.001:
    res = b - np.dot(A, x)
    p = np.dot(np.transpose(res),A)
    q = np.dot(p,res)
    alp = (np.dot((np.transpose(res)), res)) / float(q)
    x = x + alp*res
    count = count+1

print ("Actual solution is: ")
print (np.dot(np.linalg.inv(A),b))

print ("Iterative Solution x is:  ")
print (x)

print ("The final residue is:   ")
print (res)

print ("Number of iterations are:   ")
print (count)


import numpy as np
#% matplotlibinline
import matplotlib.pyplot as plt

"""
When computing the z-score for each sample on the data set a threshold must be
specified. Some good ‘thumb-rule’ thresholds can be: 2.5, 3, 3.5 or
more standard deviations.
"""

def detect_outliers_with_z_score(data):

    """
    Using Z score
    Formula for Z score = (Observation — Mean)/Standard Deviation

    z = (X — μ) / σ
    """
    outliers = []
    threshold = 2.4  # Some good ‘thumb-rule’ thresholds can be: 2.5, 3, 3.5
    mean = np.mean(data)
    std = np.std(data)

    for i in data:
        z_score = (i - mean)/std
        if np.abs(z_score) > threshold:
            outliers.append(i)
    return outliers

if __name__ == "__main__":

    #https: // github.com/krishnaik06/Finding-an-Outlier/blob/master/Finding % 20an % 20outlier % 20in % 20a % 20Dataset.ipynb
    dataset = [11, 10, 12, 14, 12, 15, 14, 13, 15, 102, 12, 14, 17, 19, 107, 10,
               13, 12, 14, 12, 108, 12, 11, 14, 13, 15, 10, 15, 12, 10, 14, 13, 15, 10]

    print(detect_outliers_with_z_score(dataset))
    pass


# -*- coding: utf-8 -*-
"""
Created on Sat Oct 30 10:07:52 2021

@author: Uday Talwar
"""

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

mpl.rcParams['figure.dpi'] = 4800

def mandelbrotset(steps,threshold,msize,nsize,degree):
    '''
    The underlying function is of the form z_n+1 = z_n^k + c - z_0 (initial value) = 0.

    steps = number of iterations

    threshold = upper bound to estimate divergence/convergence of given C value

    msize = number of rows in np.mgrid

    nsize = number of columns in np.mgrid

    degree = the degree/power of z_n, corresponds to k in the underlying function
    '''

    steps = int(steps)
    threshold = int(threshold)
    msize, nsize = int(msize), int(nsize)

    xmin, xmax, resox = -2,1,complex(0,msize) #the x-axis bounds of the grid and column size
    ymin, ymax, resoy = -1.5,1.5,complex(0,nsize) #y-axis bounds of the grid and row size

    x,y = np.mgrid[xmin:xmax:resox, ymin:ymax:resoy] #initiate mgrid

    c = x+1j*y #define c

    z = 0 #initialize z

    for j in range(steps): #iterate z as z_n+1 = (z_n)^2 + c, where z_0 = 0

        z = np.where(abs(z)<threshold,z, threshold+1)**(degree) + c #computes only those elements that are < 50

    req_set = (abs(z) < threshold) #filter grid for absolute values of z lower than theshold

    return req_set #return filtered grid

mbrot = mandelbrotset(100,50,1000,1000,2) #run iteration

#plot iteration
plt.clf()

plt.imshow(mbrot.T, extent = [-2, 1, -1.5, 1.5])
plt.gray()
plt.show()

#some other interesting renders with varying function forms

def mandelbrotset2(steps,threshold,msize,nsize,degree):

    '''Underlying function of the form z_n+1 = z_n^k + z_n^k-1 + c'''

    xmin, xmax, resox = -2,1,complex(0,msize)
    ymin, ymax, resoy = -1.5,1.5,complex(0,nsize)

    x,y = np.mgrid[xmin:xmax:resox, ymin:ymax:resoy]

    c = x+1j*y

    z = c

    for j in range(steps): #of the form, z_n+1 = z_n^k + z_n^k-1 + c

        z = np.where(abs(z)<threshold,z, threshold+1)**degree + \
            np.where(abs(z)<threshold,z, threshold+1)**(degree-1) + c

    req_set = (abs(z) < threshold)

    return req_set

test2 = mandelbrotset2(100,50,1000,1000,3)

plt.clf()

plt.imshow(test2.T, extent = [-2, 1, -1.5, 1.5])
plt.gray()
plt.show()

def mandelbrotset3(steps,threshold,msize,nsize,degree):

    '''Underlying function of the form z_n+1 = z_n^k + z_n^k-1 + z_n^k-2 + c'''

    xmin, xmax, resox = -2,1,complex(0,msize)
    ymin, ymax, resoy = -1.5,1.5,complex(0,nsize)

    x,y = np.mgrid[xmin:xmax:resox, ymin:ymax:resoy]

    c = x+1j*y

    z = c

    for j in range(steps): #of the form, z_n+1 = z_n^k + z_n^k-1 + z_n^k-2 + c

        z = np.where(abs(z)<threshold,z, threshold+1)**degree + \
            np.where(abs(z)<threshold,z, threshold+1)**(degree-1) + \
                np.where(abs(z)<threshold,z, threshold+1)**(degree-2) + c

    req_set = (abs(z) < threshold)

    return req_set

test3 = mandelbrotset3(100,50,1000,1000,3)

plt.clf()

plt.imshow(test3.T, extent = [-2, 1, -1.5, 1.5])
plt.gray()
plt.show()


'''
Program to QR decomposition using the Gram-Schmidt method
Developed by: SRIJITH R
RegisterNumber: 21004191
'''
import numpy as np
def qr(A):
    n,m=A.shape
    Q=np.empty((n,n))
    u=np.empty((n,n))

    u[:,0]=A[::,0]
    Q[:,0]=u[:,0]/np.linalg.norm(u[:,0])

    for i in range (1,n):
        u[:,i]=A[:,i]
        for j in range(i):
            u[:,i]-=(A[:,i]@Q[:,j])*Q[:,j]
            Q[:,i]=u[:,i]/np.linalg.norm(u[:,i])

    R = np.zeros((n,m))
    for i in range(n):
        for j in range(i,m):
            R[i,j]=A[:,j]@Q[:,i]

    print(Q)
    print(R)
a=np.array(eval(input()))
qr(a)


import numpy as np#导入一个数据处理模块
import pylab as pl#导入一个绘图模块，matplotlib下的模块
from scipy import signal
sampling_rate = 8000#采样频率为8000Hz
fft_size = 4096 #FFT处理的取样长度
t = np.arange(0, 1.0, 1.0/sampling_rate)#np.arange(起点，终点，间隔)产生1s长的取样时间
x = 100*np.sin(2*np.pi*156.25*t) + 200*np.sin(2*np.pi*234.375*t)#两个正弦波叠加，156.25HZ和234.375HZ
# N点FFT进行精确频谱分析的要求是N个取样点包含整数个取样对象的波形。因此N点FFT能够完美计算频谱对取样对象的要求是n*Fs/N（n*采样频率/FFT长度），
# 因此对8KHZ和512点而言，完美采样对象的周期最小要求是8000/512=15.625HZ,所以156.25的n为10,234.375的n为15。
xs = x[:fft_size]# 从波形数据中取样fft_size个点进行运算
xf = np.fft.rfft(xs)/fft_size# 利用np.fft.rfft()进行FFT计算，rfft()是为了更方便对实数信号进行变换，由公式可知/fft_size为了正确显示波形能量
# rfft函数的返回值是N/2+1个复数，分别表示从0(Hz)到sampling_rate/2(Hz)的分。
#于是可以通过下面的np.linspace计算出返回值中每个下标对应的真正的频率：
freqs = np.linspace(0, sampling_rate/2, fft_size/2+1)
# np.linspace(start, stop, num=50, endpoint=True, retstep=False, dtype=None)
#在指定的间隔内返回均匀间隔的数字
xfp = 20*np.log10(np.clip(np.abs(xf), 1e-20, 1e100))
#最后我们计算每个频率分量的幅值，并通过 20*np.log10()将其转换为以db单位的值。为了防止0幅值的成分造成log10无法计算，我们调用np.clip对xf的幅值进行上下限处理

#绘图显示结果
pl.figure(figsize=(8,4))
pl.subplot(211)
pl.plot(t[:fft_size], xs)
pl.xlabel(u"Time(S)")
pl.title(u"156.25Hz and 234.375Hz WaveForm And Freq")
pl.subplot(212)
pl.plot(freqs, xfp)
pl.xlabel(u"Freq(Hz)")
pl.subplots_adjust(hspace=0.4)
pl.show()

sample_frequencies,segment_times,spectrogram=signal.spectrogram(xs,nperseg=512)
pl.imshow(spectrogram)
pl.show()


#!/usr/bin/python
import numpy as np
import calculus #from cw5
import math

def arclength(g, a, b, n):
    xlist = np.linspace(a, b, n+1)
    f = np.vectorize(g)
    ylist = f(xlist)
    difflist = calculus.diff(f, a, b, n)[1]
    s = np.zeros(n+1)
    #s will hold the incremental values of each indivual arc length at first
    il = np.vectorize(incremental_lengther)
    s[1:] = il(difflist[1:])
    #now traverse backwards to sum individual points
    for i in xrange(n, -1, -1):
        s[i] = np.sum(s[:i+1])
    return s

def incremental_lengther(derivative):
    return (1 + derivative**2) ** .5

def sin(x):
    return math.sin(x)

def zeroX(x):
    return 0

def test_arclength(f1 = sin, f2 = zeroX):
    """
    Checks that for sin(x) and f(x) = 0, the arclengths
    are identical when using the mesh that increments by pi

    """
    assert (arclength(f1, 0, 100*math.pi, 100) == arclength(f2, 0, 100*math.pi, 100)).all()


import numpy as np

class ellipsoid:
  def __init__(self,a=0,denf=0):
    self.a=a
    self.denf=denf
    self.setGRS80()

  def setGRS80(self):
    self.a=6378137
    self.denf=298.2572221
    self.prepare()
  def prepare(self):
    self.b=self.a*(1-1/self.denf)
    self.e=np.sqrt(1- self.b**2/(self.a**2))
    self.e2=np.sqrt(self.a**2/self.b**2- 1)
  def f(self):
    return 1/denf

  def N(self,lat):
    aux=self.e*np.sin(lat)
    N=self.a/np.sqrt(1-aux*aux)
    return N
  def M(self,lat):
    e=self.e
    M=a*(1-e**2)/(1-e**2 *np.sin(lat)*np.sin(lat))**1.5
    return M
  def xyz2lonlath(self,xyz):
    xyz=np.array(xyz)
    lon=np.arctan(xyz[1]/xyz[0]);
    p=np.sqrt(xyz[0]*xyz[0]+xyz[1]*xyz[1]);
    e=self.e;
    e2=self.e2;
    b=self.b;
    u=np.arctan(xyz[2]*self.a/(p*b));
    lat=np.arctan((xyz[2]+e2*e2*b*np.sin(u)**3)/(p-e*e*self.a*np.cos(u)**3));
    N0=self.a/np.sqrt(1-e*e*np.sin(lat)*np.sin(lat));
    h=p/np.cos(lat)-N0;
    return np.array([lon*180/np.pi,lat*180/np.pi, h])
  def xyz2lonlathIter(self, xyz):
    lon=np.arctan(xyz[1]/xyz[0]);
    p=np.sqrt(xyz[0]*xyz[0]+xyz[1]*xyz[1]);
    e=self.e;
    error=1;
    h=0; N0=1; lat0=1000;
    while (error>1e-10):
      lat1=np.arctan(xyz[2]/p/(1-e*e*N0/(N0+h)));
      N0=self.a/np.sqrt(1-e*e*np.sin(lat1)*np.sin(lat1));
      h=p/np.cos(lat1)-N0;
      error=abs(lat1-lat0);
      lat0=lat1;
    return np.array([lon*180/np.pi,lat0*180/np.pi, h])
  def lonlath2xyz(self,lonlath):
    lat=lonlath[1]/180*np.pi
    lon=lonlath[0]/180*np.pi
    h=lonlath[2]
    e=self.e;
    N=self.N(lat);
    x=(N+h)*np.cos(lat)*np.cos(lon);
    y=(N+h)*np.cos(lat)*np.sin(lon);
    z=(N*(1-e*e)+h)*np.sin(lat);
    return np.array([x,y,z])

if __name__=="__main__":
    grs80=ellipsoid()
    xyz=[5176588.653, -3618162.163, -887363.920 ]
    lonlath=grs80.xyz2lonlath(xyz)
    print(xyz)
    print(lonlath)
    print(grs80.xyz2lonlathIter(xyz))
    print(grs80.lonlath2xyz(lonlath))


import scipy.constants as constants
import numpy as np
import math

class QHOGas():

    """Generates the statistics of a gas of N quantum harmonic oscillators
    at thermal equilibrium at temperature T and frequency w """
    def __init__(self, N, T=0.00001, w=1000000):
        self.N = N
        self.T = T
        self.w = w

    def p_n(self, n):
        """
        Calculates the probabilities of finding a quantum harmonic
        oscillator with quantum numbers n
        """
        bhw = (constants.hbar * self.w)/(constants.k * self.T)
        Z_1 = 1/( 2*np.sinh(bhw/2) )
        return np.exp( -bhw*(n + 1/2) )/Z_1

    def calculate_expected_n(self, ns = None):
        """
        Calculates the expected quantum number.
        Returns the theoretical value if no samples are provided
        """
        if ns is None:
            bhw = (constants.hbar * self.w)/(constants.k * self.T)
            return 1/( math.exp(bhw) - 1 )
        else:
            return np.average(ns)

    def calculate_expected_e(self, ns = None):
        """
        Calculates the expected energy per oscillator.
        Returns the theoretical value if no samples are provided
        """
        if ns is None:
            n = self.calculate_expected_n()
            return constants.hbar*self.w*( n + 1/2 )
        else:
            # Compute the energy of each oscillator and then average
            return np.average( constants.hbar*self.w*( ns + 1/2 ) )

    def calculate_internal_energy(self, ns = None):
        """
        Calculates the expected energy (which equals to the internal energy).
        Returns the theoretical value if no samples are provided
        """
        if ns is None:
            bhw = (constants.hbar * self.w)/(constants.k * self.T)
            return self.N*constants.hbar*self.w*( 1/2 + 1/( math.exp(bhw) - 1 ) )
        else:
            # Compute the total energy of each batch and then average
            total_energy_per_gas = np.sum(constants.hbar*self.w*( ns + 1/2 ), axis=1 )
            return np.average(total_energy_per_gas)

    def generate(self, amount, normalize=True, n_max=10):
        """
        Generates 'amount' states of this gas where the quantum numbers are
        limited to a max of 'n_max'.
        The states can be normalized so that the quantum numbers returned
        range from 0 to 1 where one corresponds to n=n_max.
        """
        quantum_numbers = self.sample_quantum_numbers(amount, n_max)
        return quantum_numbers/n_max if normalize else quantum_numbers

    def sample_quantum_numbers(self, batchsize, n_max):
        """
        Generates the quantum numbers of 'amount' systems of N quantum harmonic
        oscillators each.
        The result is a 2-D tensor of shape (amount, N)
        """
        # Calculate the probability for the first n_max quantum numbers and normalize
        p = self.p_n( np.arange(0, n_max) )
        accumulated = np.cumsum(p)
        accumulated /= max(accumulated)
        # Get a uniform distribution to do the usual trick
        j = np.random.uniform(size=(batchsize, self.N))
        # Sample with this auxiliar distribution
        quantum_numbers = np.zeros( j.shape )
        prev = 0
        for i, val in enumerate(accumulated):
            # Set this quantum number i if the random number j
            # is in the range of probabilities of this quantum number
            indexes = np.logical_and(j>prev, j<val)
            quantum_numbers[indexes] = i
            prev = val
        return quantum_numbers


from math import sqrt, exp, log, sin, pi
from sympy import Symbol, Limit, Derivative, solve, sympify, S, simplify, lambdify, pprint, init_printing, symbols
from fractions import Fraction

init_printing( order = 'rev-lex', use_unicode = True )

# 4.1.1

# Suppose a particle’s position is given by f (t) = t 4,
# where t is measured in seconds and f (t) is given in centimeters. What is the velocity of the particle when t = 3?

Ft = t**4
t = Symbol( 't' )
Dt = Derivative( Ft, t ).doit()
Dt.subs( { t: 3 } )

# 4.1.2

# Suppose f(x) = x^-3. What is f'(x) ?

Fx = x**-3
x = Symbol( 'x' )
Derivative( Fx, x ).doit()

# 4.1.3

# Suppose f(x) = x^0. What is f'(x) ?

Fx = x**0.1
x = Symbol( 'x' )
Derivative( Fx, x ).doit()

# 4.1.6

# Suppose a particle's position is given by f(t) = t^4,
# where t is measurd in seconds and f(t) is given in meters.
# At what time is the velocity of the particle equal to 0 ?

Ft = t**4
t = Symbol( 't' )
Dt = Derivative( Ft, t ).doit()
sln = Dt.subs( { t: 1/2 } )

# 4.1.8

# Find the derivitve: -pi/3 ( sqrt(3) )

Fq = -1 * pi/3 * ( ( q ** 3 ) ** 1/2 )
q = Symbol( 'q' )
Derivative( Fq, q ).doit()

# 4.1.9

# Suppose f(x) = x^9. What is f'(x) ?

Fx = x**9
x = Symbol( 'x' )
Derivative( Fx, x ).doit()


import numpy as np

from simple_object_detection.typing import Point2D

def euclidean_norm(p: Point2D, q: Point2D) -> float:
    """Calcula la distancia euclídea entre 2 puntos.

    :param p: punto 1.
    :param q: punto 2.
    :return: distancia euclídea.
    """
    return np.linalg.norm(np.array(p) - np.array(q))


"""
A tetrahedral number[https://en.wikipedia.org/wiki/Tetrahedral_number] is is a figurate number that represents a
pyramid with a triangular base and three sides.
Write a program to find the base of the tetrahedron that contains an input number of balls.
example: 169179692512835000 balls
taken from programmingpraxis.com
"""

import sympy
# import math

solve = sympy.solvers.solve
x = sympy.Symbol('x')

def get_tetrahydral_level(num):
    return int(solve((x**3 + (3 * x**2) + (2 * x))/6 - num, x)[0])

def calc_triangular_number(num):
    return int(num * (num + 1) / 2)

if __name__ == '__main__':
    while True:
        inp = int(input('Enter number: '))
        try:
            n = get_tetrahydral_level(inp)
            tri = calc_triangular_number(n)
            print(tri)
        except TypeError:
            print('Non-valid tetrahydral number')

# Super slow method
# def nCr(n,r):
#     f = math.factorial
#     return f(n) / (f(r) * f(n-r))
#
# if __name__ == '__main__':
#     n = 1
#     inp = int(input('Enter number: '))
#     tri = nCr(n+1,2)
#     while tri != inp and inp > 0:
#         print(inp)
#         inp -= tri
#         n += 1
#         tri = nCr(n+1,2)
#
#     if inp >= 0:
#         print('Base: {}'.format(inp))
#     else:
#         print('Non tetrahedral number')


import numpy as np

from mygrad import Tensor
from mygrad.operation_base import Operation

__all__ = ["selu"]

_ALPHA = 1.6732632423543772848170429916717
_SCALE = 1.0507009873554804934193349852946

class SELU(Operation):
    """ Returns the scaled exponential linear activation (SELU) elementwise along x. The SELU is
    given by  λɑ(exp(x) - 1) for x < 0 and λx for x ≥ 0.

    Notes
    -----
    The SELU activation was proposed in the paper
        Self-Normalizing Neural Networks
        Günter Klambauer, Thomas Unterthiner, Andreas Mayr, Sepp Hochreiter
    at https://arxiv.org/abs/1706.02515
    """

    def __call__(self, x):
        """
        Parameters
        ----------
        x : mygrad.Tensor
            Input data.

        Returns
        -------
        numpy.ndarray
            The SELU function applied to `x` elementwise.
        """
        self.variables = (x,)

        x = x.data
        self.exp = _ALPHA * (np.exp(x) - 1)
        return _SCALE * np.where(x < 0, self.exp, x)

    def backward_var(self, grad, index, **kwargs):
        x = self.variables[index]
        return grad * _SCALE * np.where(x.data < 0, self.exp + _ALPHA, 1)

def selu(x, constant=False):
    """ Returns the scaled exponential linear activation (SELU) elementwise along x.

    The SELU is given by  λɑ(exp(x) - 1) for x < 0 and λx for x ≥ 0.

    Parameters
    ----------
    x : mygrad.Tensor
        Input data.

    constant : bool, optional(default=False)
        If ``True``, the returned tensor is a constant (it
        does not back-propagate a gradient)

    Returns
    -------
    mygrad.Tensor
        The SELU function applied to `x` elementwise.

    Notes
    -----
    The SELU activation was proposed in the paper
        Self-Normalizing Neural Networks
        Günter Klambauer, Thomas Unterthiner, Andreas Mayr, Sepp Hochreiter
    at https://arxiv.org/abs/1706.02515

    Examples
    --------
    >>> import mygrad as mg
    >>> from mygrad.nnet.activations import selu
    >>> x = mg.arange(-5, 6)
    >>> x
    Tensor([-5, -4, -3, -2, -1,  0,  1,  2,  3,  4,  5])
    >>> y = elu(x, alpha=0.1); y
    Tensor([-1.74625336, -1.72589863, -1.67056873, -1.52016647, -1.11133074,
         0.        ,  1.05070099,  2.10140197,  3.15210296,  4.20280395,
         5.25350494])
    """
    return Tensor._op(SELU, x, constant=constant)


import numpy as np
from mayavi import mlab
from plyfile import PlyData

def show_mayavi(x, y, z, res):
    fig = mlab.figure(bgcolor=(1, 1, 1), size=(640, 480))
    mlab.outline(color=(0, 0, 0))

    res = np.asarray(res)
    xx, yy = np.mgrid[-10:10:0.5, -10:10:0.5]
    zz = res[0] * xx + res[1] * yy + res[2]

    # z /= 4

    # mlab.surf(xx, yy, zz, color=(0, 0, 1), warp_scale=.25, representation='wireframe', line_width=0.5)
    # mlab.points3d(x, y, z, color=(1, 0, 0), scale_factor=.3, figure=fig)
    mlab.points3d(x, y, z, color=(1, 0, 0), mode='point', figure=fig)

    axes = mlab.axes(color=(0, 0, 0), nb_labels=5)
    axes.title_text_property.color = (0.0, 0.0, 0.0)
    axes.title_text_property.font_family = 'times'
    axes.label_text_property.color = (0.0, 0.0, 0.0)
    axes.label_text_property.font_family = 'times'
    mlab.gcf().scene.parallel_projection = True
    mlab.orientation_axes()
    mlab.show()

def random_point(a, b, c):
    x = np.random.uniform(-10, 10, size=100)
    y = np.random.uniform(-10, 10, size=100)
    z = (a * x + b * y + c) + np.random.normal(-1, 1, size=100)
    return x, y, z

def read_ply(filepath):
    _data = PlyData.read(filepath)
    _pc = _data['vertex'].data
    _array = np.array([_pc['x'], _pc['y'], _pc['z']])
    return _array

def LSM(x, y, z):
    tmp_a = []
    tmp_b = []
    for i in range(len(x)):
        tmp_a.append([x[i], y[i], 1])
        tmp_b.append(z[i])
    bt = np.matrix(tmp_b).T
    a = np.matrix(tmp_a)
    res = (a.T * a).I * a.T * bt
    print('平面拟合结果为：z = %.3f * x + %.3f * y + %.3f' % (res[0], res[1], res[2]))
    return res

if __name__ == '__main__':
    # _x, _y, _z = random_point(2, 5, 6)
    _x, _y, _z = read_ply('./1.ply')
    _lsm = LSM(_x, _y, _z)

    show_mayavi(_x, _y, _z, _lsm)


import numpy as np

'''
softmaxOutputs = [[0.7, 0.1, 0.2],
                  [0.1, 0.5, 0.4],
                  [0.02, 0.9, 0.08]]

classTargets = [0, 1, 1]

for targ_idx, distribution in zip(classTargets, softmaxOutputs):
    print(distribution[targ_idx])
'''

softmaxOutputs = np.array([[0.7, 0.1, 0.2],
                           [0.1, 0.5, 0.4],
                           [0.002, 0.9, 0.08]])
classTargets = [0, 1, 1]

# print(softmaxOutputs[[0, 1, 2], classTargets])
print(softmaxOutputs[range(len(softmaxOutputs)), classTargets])

# Loss of distribution
Loss = -np.log(softmaxOutputs[
    range(len(softmaxOutputs)), classTargets
])

print('-log: ', Loss)

averageLoss = np.mean(Loss)
print('Average -log: ', averageLoss)

Chapter 5 - Calculating Loss/retrive-values-in-distribution.py


import matplotlib.pyplot as plt
import numpy as np
from numba import jit
from timeit import default_timer as timer
import random
start = timer()

# PARAMETERS TO CHANGE THE FRACTAL GENERATED
seq = "AB"                     # sequence to alternate r values
probability = 75               # probability that a given value in the sequence will switch
a_lb = 2                       # a lower bound
a_ub = 4                       # a upper bound
b_lb = 2                       # b lower bound
b_ub = 4                       # b upper bound

# PARAMETERS REFINING ACCURACY OF FRACTAL PICTURE GENERATED
num_warmups = 1200             # number of "warmups" or throwaway iterations before computing lyapunov exponent
num_lyap_iterations = 300      # number of iterations used to compute the lyapunov exp
steps = 500                    # steps between b1 and b2 values on axes -- higher it is, the better the picture

# CREATING RANDOM SEQUENCE WITH A LENGTH OF THE TOTAL NUMBER OF ITERATIONS
# EACH ITERATION THE PROBABILITY WILL BE JUDGED AGAINST THIS LIST
@jit
def getrandomseq():
    problist = list()
    for i in range(num_lyap_iterations + num_warmups):
        problist.append(random.randint(0,99))
    return problist

# LOGISTIC MAP THAT GIVES US THE NEXT X
@jit
def F(x, curr_r):
    return (curr_r * x) * (1 - x)

# DERIVATIVE OF F -- USED TO COMPUTE THE LYAPUNOV EXPONENT
@jit
def Fprime(x, curr_r):
    ans = curr_r * (1 - (2 *x))
    ans[ans == 0] = 0.0001
    ans[ans == -np.inf] = -1000
    ans[ans == np.inf] = 1000
    return ans

# RETURNS THE CORRECT B-VALUE BASED ON THE CURRENT ITERATION
@jit
def getseqval(curr_iteration, a, b, probability, problist):
    randnum = problist[curr_iteration]
    index = np.mod(curr_iteration, len(seq))
    if (seq[index] == 'A'):
        if (probability <= randnum):
            return a
        else:
            return b
    else:
        if (probability <= randnum):
            return b
        else:
            return a

# RETURNS THE LYAPUNOV EXPONENT BASED ON THE SPECIFIED B1 AND B2 VALUES
@jit
def getlyapexponent(time_scale, probability, problist):
    b1, b2 = time_scale
    lyap_prob = probability

    x = .5          # initial value of x
    lyapsum = 0     # initializing lyapunov sum for use later

    # do warmups, to discard the early values of the iteration to allow the orbit to settle down
    for i in range(num_warmups):
        x = F(x, getseqval(i, b1, b2, lyap_prob, problist))

    for i in range(num_warmups, num_lyap_iterations + num_warmups):
        lyapsum += np.log( np.abs(Fprime(x, getseqval(i, b1, b2, lyap_prob, problist) ) ) )
        # get next x
        x = F(x, getseqval(i, b1, b2, lyap_prob, problist))

    return (lyapsum / num_lyap_iterations)

# CREATING FRACTAL IMAGE
a = np.linspace(a_lb, a_ub, steps)   #range of b1 values
b = np.linspace(b_lb, b_ub, steps)   #range of b2 values
aa, bb = np.meshgrid(a, b)

# COMPUTING AVERAGE IMAGE FROM MULTIPLE RANDOM SEQUENCES
lyap_exponents = []
for i in range(10):
    problist = getrandomseq()
    lyap_exponents.append(getlyapexponent( (bb, aa), probability, problist ))

fractal_grid = np.average(lyap_exponents, axis = 0)

 # CREATING AND ADJUSTING GRAPH
plt.figure()       # creating new window for each graph that is run

plt.subplots_adjust(top = 0.825, bottom = 0.1, left = 0.11, right = 0.9, hspace = 0.2, wspace = 0.2)
plt.suptitle("Probabilistic Lyanpuov fractal for logistic map")
plt.title("Pattern: " + seq + "\n Probability that a given value\n in the pattern will switch: " + str(probability) + "%")

plt.xlabel("a")
plt.ylabel("b")

im = plt.imshow(fractal_grid, cmap = lyap_cmap, vmin = -2, vmax = 0, origin = "lower", extent = (a_lb, a_ub, b_lb, b_ub))
plt.colorbar(im)

end = timer()

print("elapsed time: " + str(end - start))


import numpy as np

# 计算sigmoid函数值
def sigmoid(z):
	g = 1/(1+np.exp(-z))

	return g

def sigmoid_gradient(z):
	grad = sigmoid(z) * (1-sigmoid(z))

	return grad


import numpy as np

def empty(grid):
    # check if there is any place empty i.e. equals to 0
    for row in range(9):
        for col in range(9):
            if grid[row][col] == 0:
                return (row, col)

    return None

def possible(grid, row, col, n):

    # get the position of the box
    box_x = (col//3)*3
    box_y = (row//3)*3

    # iterate over row and columns
    for i in range(9):
        if grid[row][i] == n or grid[i][col] == n:
            return False

    # itererate over the box
    for i in range(3):
        for j in range(3):
            if grid[box_y+i][box_x+j] == n:
                return False

    return True

def solve(grid):
    position = empty(grid)

    if not position:
        return True
    else:
        row, col = position

    for n in range(1, 10):
        if possible(grid, row, col, n):
            grid[row][col] = n

            if solve(grid):
                return True

            grid[row][col] = 0

    return False

if __name__ == "__main__":

    grid = [[7,8,0,4,0,0,1,2,0],
            [6,0,0,0,7,5,0,0,9],
            [0,0,0,6,0,1,0,7,8],
            [0,0,7,0,4,0,2,6,0],
            [0,0,1,0,5,0,9,3,0],
            [9,0,4,0,6,0,0,0,5],
            [0,7,0,3,0,0,0,1,2],
            [1,2,0,0,0,7,4,0,0],
            [0,4,9,2,0,6,0,0,7]]

    solve(grid)

    print(np.matrix(grid))


import numpy as np
import sys

'''
Function:  Normalization
Description: Normalize input data. For vector x, the normalization process is given by
            normalization(x) = (x - min(x))/(max(x) - min(x))
Input:  data        dataType: ndarray   description: input data
Output: normdata    dataType: ndarray   description: output data after normalization
'''

def Normalization(data):
    # get the max and min value of each column
    minValue = data.min(axis=0)
    maxValue = data.max(axis=0)
    diff = maxValue - minValue
    # normalization
    mindata = np.tile(minValue, (data.shape[0], 1))
    normdata = (data - mindata) / np.tile(diff, (data.shape[0], 1))
    return normdata

'''
Function:  Standardization
Description: Standardize input data. For vector x, the normalization process is given by
             Standardization(x) = x - mean(x)/std(x)
Input:  data            dataType: ndarray   description: input data
Output: standarddata    dataType: ndarray   description: output data after standardization
'''

def Standardization(data):
    # get the mean and the variance of each column
    meanValue = data.mean(axis=0)
    varValue = data.std(axis=0)
    standarddata = (data - np.tile(meanValue, (data.shape[0], 1))) / np.tile(varValue, (data.shape[0], 1))
    return standarddata

'''
Function:  calcuateDistance
Description: calcuate the distance between input vector and train data
Input:  x1      dataType: ndarray   description: input vector
        x2      dataType: ndarray   description: input vector
Output: d       dataType: float     description: distance between input vectors
'''
def calculateDistance(distance_type, x1, x2):
    if distance_type == "Euclidean":
        d = np.sqrt(np.sum(np.power(x1 - x2, 2), axis=1))
    elif distance_type == "Cosine":
        d = np.dot(x1, x2)/(np.linalg.norm(x1)*np.linalg.norm(x2))
    elif distance_type == "Manhattan":
        d = np.sum(x1 - x2)
    else:
        print("Error Type!")
        sys.exit()
    return d

'''
Function:  calcuateDistance
Description: calcuate the distance between input vector and train data
Input:  input       dataType: ndarray   description: input vector
        traind_ata  dataType: ndarray   description: data for training
        train_label dataType: ndarray   description: labels of train data
        k           dataType: int       description: select the first k distances
Output: prob        dataType: float     description: max probability of prediction
        label       dataType: int       description: prediction label of input vector
'''
# def calculateDistance(input, train_data, train_label, k):
#     train_num = train_data.shape[0]
#     # calcuate the distances
#     distances = np.tile(input, (train_num, 1)) - train_data
#     distances = distances**2
#     distances = distances.sum(axis=1)
#     distances = distances**0.5

#     # get the labels of the first k distances
#     disIndex = distances.argsort()
#     labelCount = {}
#     for i in range(k):
#         label = train_label[disIndex[i]]
#         labelCount[label] = labelCount.get(label, 0) + 1

#     prediction = sorted(labelCount.items(), key=op.itemgetter(1), reverse=True)
#     label = prediction[0][0]
#     prob = prediction[0][1]/k
#     return label, prob

'''
Function:  calculateAccuracy
Description: show detection result
Input:  test_data  dataType: ndarray   description: data for test
        test_label dataType: ndarray   description: labels of test data
Output: accuracy   dataType: float     description: detection accuarcy
'''
def calculateAccuracy(test_label, prediction):
    test_label = np.expand_dims(test_label, axis=1)
    # prediction = self.prediction
    accuracy = sum(prediction == test_label)/len(test_label)
    return accuracy


import sympy as sym
import scipy as sci
import numpy as np
from math import *
import sys

from lib_newton import *

A             = np.zeros((1,0));
n             = 0;
eps           = 0;
wholeSize     = 1;

# Load ma trận từ file
while(n * n != wholeSize):
#{
    print("Đang tải ma trận.....");
    A = np.loadtxt("input.txt");
    A = np.ravel(A);

    # Tự sinh kích thước ma trận
    wholeSize = np.shape(A)[0];
    n = int(sqrt(wholeSize));

    if (n * n != wholeSize):
    #{
        c = input("Kích cỡ ma trận không hợp lệ. Vui lòng chỉnh sửa file input.txt gõ \"Y\" để tải lại chương trình hoặc gõ \"N\" để thoát chương trình:");
        if(c == "N"):
            exit();
    #}
    else:
        A = np.reshape(A, (n,n));
        print("Tải ma trận thành công.")
#}

# Nhập sai số eps
eps           = float(input("Nhập sai số eps: "));

# In ra ma trận A
print("--------------------------------------------------------------------");
print("Ma trận A:");
print(A);

# Nghịch đảo ma trận
uu = newton_mat_inversion(A, n, eps);
A1 = uu.improved_newton();

# In ra ma trận nghịch đảo
print("--------------------------------------------------------------------");
print("Ma trận nghịch đảo của A:");
print(A1);

# Kiểm tra nhân ngược
print("--------------------------------------------------------------------");
print("Kiểm tra nhân ngược:");
print(A1 @ A);

Topic 2.5 - Matrix Inversion/14.1.Newton/newton_fromfile.py


#! /usr/bin/python

"""
This programme solves an online coding challenge of manupulating arrays based on prime numbers

Version 1.0

Author: Billy Shi
Date: Dec 24, 2021
"""

### Problem Statement (paraphased)
"""
One row of lights start with a current state, each can be turned on (1) or off (0). This is provided
in an array called /states/. Another integer is provided called /number/.
For this number, we change the state of the light that correspond to the multiples of its prime factors.

For example: if initial /states/ is [1, 1, 1, 1, 1, 1]
Number is 6 with prime factors 2 and 3
We change the states of lights 2, 4, 6 because they are multiples of 2
We then change states of lights 3, 6 because that is multiple of 3

For a more advanced version, the variable /numbers/ can contain an array of numbers.
Each number carries out the above steps.

Output the final states of lights in an array.
"""

### Solution Overview
"""
Approach this solution by breaking down steps:
1. For each number in /numbers/, get its prime factors
2. For each prime factor, work out the lights that need state change
3. Change light state and store it for next iteration
"""

import numpy as np

def PrimeLight(states, numbers):
    # function to obtain all prime factors
    def primefactor(n):
        res = []
        # if n is even
        while n % 2 == 0:
            if 2 not in res:
                res.append(2)
            n = n / 2
        # now n must be even
        for i in range(3,int(np.sqrt(n))+1,2):
            while n % i == 0:
                if i not in res:
                    res.append(i)
                n = n / i
        # finally include n itself
        if n > 2 and n not in res:
            res.append(int(n))
        return res
    # obtain prime factors for numbers
    for i in range(len(numbers)):
        numbers[i] = primefactor(numbers[i])
        temp = []
        for prime in numbers[i]:
            temp = temp + list(np.arange(prime, prime*(len(states)//prime)+1, prime))
        numbers[i] = temp
        # now the variable numbers contain lights needing state change
        for l in numbers[i]:
            if states[l-1] == 1:
                states[l-1] = 0
            elif states[l-1] == 0:
                states[l-1] = 1

    return states

# Run program
states = [1,1,0,0,1,1,0,1,1,1]
numbers = [3,4,15]
assert PrimeLight(states, numbers) == [1,0,0,1,0,0,0,0,1,1]


from warnings import warn
import numpy as np
import numpy.linalg as la

from modpy.optimize._optim_util import OptimizeResult
from modpy.optimize._constraints import prepare_bounds

TERMINATION_MESSAGES = {
    -1: 'LinAlgError due to indeterminate system.',
    0: 'maximum number of iterations reached.',
    1: 'not implemented yet.',
    2: 'not implemented yet.',
    3: 'the unconstrained solution is optimal.'
}

def lsq_linear(A, b, W=None, bounds=None, method='', tol=1e-10, max_iter=100):
    """
    Solves a weighted linear least squares problem with bounds on the variables.

    Given an m-by-n system matrix, A, and a target vector with m elements,
    solves the least squares formulation::

        min 1/2 * ||A x - b|| ** 2
        s.t. lb <= x <= ub

    with unconstrained solution (ordinary)::

        x = (A'A) **(-1) A'b

    with unconstrained solution (weighted)::

        x = (A'WA) **(-1) A'Wb

    Parameters
    ----------
    A : array_like, shape (n, m)
        System matrix
    b : array_like, shape (n,)
        Target vector
    W : array_like, shape (n, n), optional
        Weight matrix
    bounds : 2-tuple of array_like, optional
        Lower and upper bounds. Both lower and upper bound must
        have shape (n,) or be a scalar. Use np.inf for no bounds
    method : string, optional
        TODO: allow Trust Region Reflective algorithm
    tol : float, optional
        Tolerance parameter related to convergence of iterative bounded solver
    max_iter : None or int, optional
        Maximum number of iterations during iterative bounded solver

    Returns
    -------
    OptimizeResult with the following fields:
    x : array_like, shape (m,)
        Solution vector.
    success : bool,
        True if algorithm converged within its optimality conditions.
    status : int
        Reason for algorithm termination:

            * -1 : LinAlgError due to indeterminate system matrix (or weights)
            *  0 : Maximum number of iterations reached (TODO: for trust-region)
            *  1 : TODO: first order optimality reached (trust-region)
            *  2 : TODO : relative change of cost function (trust-region)
            *  3 : Unconstrained solution is optimal

    message : str
        Description of the termination reason.
    nit : int
        Number of iterations used to converge. 0 in the unconstrained case (no iterations required).
    """

    n, m = A.shape

    b = np.atleast_1d(b)
    if b.ndim != 1:
        if (b.ndim == 2) and (np.prod(b.shape) == b.size):
            b = np.reshape(b, (b.size,))
        else:
            raise ValueError('`b` must have at most 1 dimension')

    if b.size != n:
        raise ValueError('Inconsistent shapes between `A` and `b`.')

    if (W is not None) and ((W.shape[0] != n) or (W.shape[1] != n)):
        raise ValueError('Inconsistent shapes between `A` and `W`.')

    if bounds is not None:
        lb, ub = prepare_bounds(bounds, n)

    try:

        if W is None:
            x, cond = _ordinary_lsq(A, b)
        else:
            x, cond = _weighted_lsq(A, b, W)

        res = OptimizeResult(x, success=True, status=3, nit=0, cond=cond)

    except la.LinAlgError:
        res = OptimizeResult(None, success=False, status=-1, nit=0)

    res.message = TERMINATION_MESSAGES[res.status]

    return res

def _weighted_lsq(A, b, W):
    ATW = A.T @ W
    Q, R = la.qr(ATW @ A)
    return la.solve(R, Q.T @ (ATW @ b)), _condition(R)

def _ordinary_lsq(A, b):
    Q, R = la.qr(A.T @ A)
    return la.solve(R, Q.T @ (A.T @ b)), _condition(R)

def _condition(R):
    cond = la.cond(R)
    if cond > 1e5:
        warn('The condition number of R in the LSQ solver is: {}'.format(cond))

    return cond


# Run a test to evaluate parameters of 2d gaussian through FFT/PSD/ACovF.
# This is useful, because a Gaussian should be analytically predictable through each of these transformations.
# Summary:
# The FFT of a gaussian is a Gaussian, with sigma_fft (in frequency space) = 1/(2*pi*sigma_x).
#  Note that a gaussian also translates to a real-only FFT (imaginary part = 0), if using analytic, perfect transform.
# Then the 2d PSD is created by squaring the (real+imaginary) FFT - and a gaussian squared is a gaussian with sigma
#   divided by sqrt 2. So the sigma_psd2d = sigma_fft / sqrt(2) (note this is still in frequency).
# The 2d ACovF is calculated by taking the FFT of the PSD, thus translating back to spatial coordinates .. so
#  sigma_acovf = 1/(2*pi*sigma_psd2d) = 1/(2*pi*sigma_FFT)*sqrt(2) = sigma_x*sqrt(2)
# Looking at the 1d version of the ACovF, we should still find a gaussian with same sigma, and same for 1d PSD in frequency.
#  But, if we want to translate the PSD into 'spatial' scales related to pixels, we can look at the width of the gaussian
#  and try to translate that to pixel scales. The result is that sigma_psd(pix) = sqrt(nx*ny)/(sigma_psd(freq)*2*pi)

import numpy
import pylab
import testImage as ti
from scipy.optimize import curve_fit
from scipy import fftpack

# Gaussian function (for fitting).
def gaussian(x, *p):
    A, mu, sigma = p
    return A*numpy.exp(-(x-mu)**2/(2.*sigma**2))
# Simple function to generate expected and fit data values.
def dofit(x, d, mean, sigma):
    p = (d.max().real, mean, sigma)
    coeff, var_matrix = curve_fit(gaussian, x, d, p0=p)
    fitval = gaussian(x, *coeff)
    expval = gaussian(x, *p)
    print '  Fit:', coeff,  ' Exp:', p
    return fitval, expval
def doplot(x, d, fitval, expval, title):
    pylab.figure()
    pylab.plot(x, d, 'b.', label='data')
    pylab.plot(x, fitval, 'r-', label='fit')
    pylab.plot(x, expval, 'k:', label='expected')
    pylab.legend(numpoints=1, fancybox=True, fontsize='small')
    pylab.title(title)
    return

# Step 1 - generate 2d gaussian image.
# Generate 2d gaussian image of size 'size' and with sigma 'sigma_x'.
size = 750
sigma_x = 5.0
gauss = ti.TestImage(shift=True, nx = size, ny=size)
gauss.addGaussian(xwidth=sigma_x, ywidth=sigma_x, value=1.0)
gauss.zeroPad()
#gauss.hanningFilter()  # not really necessary for a gaussian which doesn't touch the edges of the image.
# and calculate FFT/PSD2d/PSD1d/ACovF2d/ACovF1d.
gauss.calcAll(min_npix=2, min_dr=1.0)

print 'Using image size of %.0f (but with zeroPadding to get to %.0f) and sigma of %.2f' %(size, gauss.nx, sigma_x)

# Step 2 - analyze the width of the gaussians for each of the original image/FFT/PSD/ACovF.

# Look at original image, and slice through center.
print 'Plotting Image'
gauss.showImage()
# Get x data for 1-d gaussian fit to data from the center of the image
x = numpy.arange(0, gauss.nx, 1.0)
# Get 'y' (image) data for 1-d gaussian fit
d = gauss.image[round(gauss.ny/2.0)][:]
# Set expected values for fit ...
mean = gauss.nx/2.0
sigma = sigma_x
fitval, expval = dofit(x, d, mean, sigma)
doplot(x, d, fitval, expval, 'Image slice')

# Move on to FFT
print 'Plotting FFT'
gauss.showFft(real=True, imag=True)
# Slice through FFT ... note that we're now dealing with FREQUENCY space
#   w (freq) = 1/(2*PI*x)
x = gauss.xfreq
d = fftpack.ifftshift(gauss.fimage)[0][:].real
d = numpy.abs(d)
idx = numpy.argsort(x)
d = d[idx]
x = x[idx]
# So expect gaussian to FT to another gaussian, but now with mu=0 and sigma=1/(2*PI*sigma_x)
mean = 0
sigma_fft = 1/(2.0*numpy.pi*sigma_x)
fitval, expval = dofit(x, d, mean, sigma_fft)
doplot(x, d, fitval, expval, 'FFT slice')
pylab.xlim(-.2, .2)

# Now look at PSD - in frequency space first.
print 'Plotting PSD frequency scale'
gauss.showPsd2d()
d = fftpack.ifftshift(gauss.psd2d)[0][:].real
d = d[idx]
# because PSD is square of FFT (which was a gaussian), expect mu = 0 but
#  sigma = sigma_fft / sqrt(2) == (1/sigma_x*2*PI)*1/sqrt(2)
mean = 0
sigma_psd_freq = sigma_fft/numpy.sqrt(2)
fitval, expval = dofit(x, d, mean, sigma_psd_freq)
doplot(x, d, fitval, expval, 'PSD2d slice')
pylab.xlim(-.2, .2)

# And now look at 2d psd in 'pixel' scales
print 'Plotting PSD2d spatial scale'
x = numpy.arange(-gauss.xcen, gauss.nx-gauss.xcen, 1.0)
d = gauss.psd2d[round(gauss.ny/2.0)][:].real
# Because of pixel 'scaling' (and is tied to total size of image) expect sigma = sqrt(nx*ny)/(sigma_x*sqrt(2)*2*PI)
mean = 0
sigma_psd_pix = 1/(sigma_x*numpy.sqrt(2))*numpy.sqrt(gauss.nx*gauss.ny)/(2.0*numpy.pi)
fitval, expval = dofit(x, d, mean, sigma_psd_pix)
doplot(x, d, fitval, expval, 'PSD2d slice, spatial scale')
pylab.xlim(-200, 200)

# Plot 1d PSD (checked this with frequency space, and it matches. However, in 'pixel space', it just doesn't really.
# This may be just a problem with my interpretation, but it does seem like there is something more fundamental here.
# For example, the distribution in pixel space is not really quite a gaussian (even fitting 1/x) - althouhg it is close.
"""
print 'plotting 1d PSD'
gauss.showPsd1d(linear=True)
x = gauss.psdx
d = gauss.psd1d.real
r = 1/x
sigma = 1/(sigma_psd_pix)
#r =x
#sigma = sigma_psd_freq
fitval, expval = dofit(r, d, 0., sigma)
doplot(x, d, fitval, expval, 'PSD1d')
"""

# Now on to the ACovF
print 'Plotting ACovF'
gauss.showAcovf2d()
x = numpy.arange(-gauss.xcen, gauss.nx-gauss.xcen, 1.)
d = gauss.acovf.real[round(gauss.ny/2.0)][:]
# Okay, ACovF is actual inverse FFT of PSD, so in pixel scales, expect sigma_acovf = 1/(sigma_psd_freq*2*PI)
#   ... or = sigma_x*sqrt(2) because of squaring the gaussian during the PSD creation.
#sigma_acovf = 1/(sigma_psd_freq*2.0*numpy.pi)
mean = 0
sigma_acovf = sigma_x*numpy.sqrt(2)
fitval, expval = dofit(x, d, mean, sigma_acovf)
doplot(x, d, fitval, expval, 'ACovF2d slice')

# And look at the 1d ACovF.
print 'Plotting acovf 1d.'
x = gauss.acovfx
d = gauss.acovf1d.real
sigma_acovf = sigma_x*numpy.sqrt(2)
fitval, expval = dofit(x, d, mean, sigma_acovf)
doplot(x, d, fitval, expval, 'ACovF1d')

pylab.show()


# -*- coding: utf-8 -*-

# Author: Daniel Yang <daniel.yj.yang@gmail.com>
#
# License: BSD 3 clause

import inspect
import numpy as np
import sympy
import torch
import matplotlib.pylab as plt

class calculus(object):
    def __init__(self):
        super().__init__()

    @staticmethod
    def derivative_using_sympy():
        x = sympy.symbols('x')
        for expr in [x, sympy.sin(x), sympy.exp(x), sympy.log(x), x**x]:
            for i in range(4):
                expr = sympy.Derivative(expr, x)
                print(f"{expr} = {expr.doit()}, when x=1.0, expression = {expr.doit().evalf(subs={x:1,})}")

    @staticmethod
    def derivative_plot_using_sympy():
        x = sympy.symbols('x')
        expr = x ** 3
        deriv = sympy.Derivative(expr, x).doit()
        x_values = np.linspace(-5, 5, 1000)
        expr_values  = np.array([expr.evalf(subs={x:x_value})  for x_value in x_values])
        deriv_values = np.array([deriv.evalf(subs={x:x_value}) for x_value in x_values])
        plt.plot(x_values, expr_values,  label = 'x ** 3')
        plt.plot(x_values, deriv_values, label='derivative (3*x**2)')
        plt.xlabel('x')
        plt.legend()
        plt.title(f"{inspect.getframeinfo(inspect.currentframe()).function}()")
        plt.show()

    @staticmethod
    def integrate_using_sympy():
        x = sympy.symbols('x')
        for expr in [x, sympy.sin(x), sympy.exp(x), sympy.log(x), x**x]:
            for i in range(4):
                expr = sympy.Integral(expr, x)
                print(f"{expr} = {expr.doit()}, when x=1.0, expression = {expr.doit().evalf(subs={x:1,})}")

    @staticmethod
    def derivative_using_torch():
        x = torch.tensor(1.0, requires_grad=True)
        expr = torch.exp(x)
        expr.backward()
        print("The dervative of exp(x) at x = 1.0: ", x.grad)

    @staticmethod
    def derivative_plot_using_torch():
        x = torch.linspace(-5, 5, 1000, requires_grad=True)
        expr = x ** 3
        sum_trick = torch.sum(expr) # a sum trick
        sum_trick.backward()
        plt.plot(x.detach().numpy(),   expr.detach().numpy(), label='x ** 3')
        plt.plot(x.detach().numpy(), x.grad.detach().numpy(), label='derivative (3*x**2)')
        plt.xlabel('x')
        plt.legend()
        plt.title(inspect.getframeinfo(inspect.currentframe()).function)
        plt.show()

def math_demo():
    calculus.derivative_plot_using_sympy()
    calculus.derivative_plot_using_torch()

    calculus.derivative_using_sympy()
    calculus.integrate_using_sympy()
    calculus.derivative_using_torch()


import math
import numpy as np

"""
STATS GENERATOR FOR TURN BASED OR ACTION RPG (ROLE PLAYING GAMES)
By: ROHMAN WIDIYANTO
GitHub: http://github.com/rohwid/

All component or object defined separately, here's the reason:
- Levels: Because sometimes the characters won't start from 1st level.
- Magic Point: Because sometimes the games doesn't need it (ex: action RPG).
- Number of Weaknesses: Same reason with Magic Point.
- Generate data container: Generate data container dynamically.

Notes:
- Anything which contain "show" in the function was used for debug or
  check the values.
"""

def round_number(number):
    if math.ceil(number) - number > number - math.floor(number):
        return math.floor(number)
    elif math.ceil(number) - number < number - math.floor(number):
        return math.ceil(number)
    else:
        return math.ceil(number)

def split_range(range_number, number_splitter, density=False):
    number = 0

    # for level and enemies
    if not density:
        number = len(number_splitter)

    # for scale density
    if density:
        number = number_splitter

    value = np.zeros(number)

    # Rounding process
    if range_number % number == 0:
        for i in range(number):
            value[i] = range_number / number
    else:
        for i in range(number):
            value[i] = range_number / number

        if number % 2 == 0:
            # 0 - 2 (if 6) - left
            even_left = 0

            for i in range(int(number / 2)):
                # extract the unrounded value
                temp_even_1 = value[i] - math.floor(value[i])
                even_left = even_left + temp_even_1
                value[i] = math.floor(value[i])

            # add to the mid of range
            value[int(number / 2) - 1] = value[int(number / 2) - 1] + round_number(even_left)

            # 3 - 5 (if 6) - right
            even_right = 0

            for i in range(int(number / 2)):
                # extract the unrounded value
                temp_even_right = value[i + int(number / 2)] - math.floor(value[i + int(number / 2)])
                even_right = even_right + temp_even_right
                value[int(number / 2) + i] = math.floor(value[int(number / 2) + i])

            # add to the mid of range
            value[int(number / 2)] = value[int(number / 2)] + round_number(even_right)
        else:
            odd = 0

            for i in range(number):
                temp_odd = value[i] - math.floor(value[i])
                odd = odd + temp_odd
                value[i] = math.floor(value[i])

            # add to the mid of value
            value[math.ceil(number / 2) - 1] = value[math.ceil(number / 2)] + round_number(odd)

    return value

def mean_values(all_values):
    sum_value = 0

    for i in range(len(all_values)):
        sum_value = sum_value + all_values[i]

    return sum_value / len(all_values)

def var_val(all_values, mean):
    sum_values = 0

    for i in range(len(all_values)):
        temp_sum_values = (all_values[i] + mean) ** 2
        sum_values = sum_values + temp_sum_values

    return sum_values / len(all_values)


# -*- coding: utf-8 -*-
import numpy as np
import cvxpy as cvx
import matplotlib.pyplot as plt
import scipy as sp

def multilevelPWM(S=[-2,-1,0,1,2], sineharmonicnums=[1,3,5,7], sinevals=[1.5,0,0,0], cosineharmonicnums=[1,3,5,7], cosinevals=[0,0,0,0], N=2048, DCval=0, solver='ECOS', plots='OFF'):
    """
    :param S: List consisting of possible voltage levels (floats).
    :param sineharmonicnums: List of sine harmonic numbers (integers).
    :param sinevals: List of sine harmonic values (floats).
    :param cosineharmonicnums: List of cosine harmonic numbers (integers).
    :param cosinevals: List of cosine harmonic values (floats).
    :param N: Time discretization (integer, default is 2048). Must be much larger than max(sineharmonicnums, sosineharmonicnums).
    :param DCval: Desired DC value (floats).
    :param solver: One of the CVXPY solvers, (default: ECOS).
    :param plots: Set it to either 'ON' or 'OFF',  (default: 'OFF).
    :return: The waveform as a list, the switching angles as a list (degrees) and the norm of the difference of
    actual and desired as a percentage of the absolute sum of desired harmonics
    """
    Fs = np.zeros([len(sineharmonicnums), N])
    Fc = np.zeros([len(cosineharmonicnums), N])

    for k in range(len(sineharmonicnums)):
        Fs[k, :] = np.sin(2 * np.pi * (np.linspace(0, N - 1, N) / N) * sineharmonicnums[k])
    for k in range(len(cosineharmonicnums)):
        Fc[k, :] = np.cos(2 * np.pi * (np.linspace(0, N - 1, N) / N) * cosineharmonicnums[k])

    Z = cvx.Variable([N, len(S)])
    s = np.array(S)

    constraints = [np.ones(N) @ (Z @ s) / N == DCval,
                   2*Fc @ (Z @ s) / N == cosinevals,
                   2*Fs @ (Z @ s) / N == sinevals,
                   Z >= 0,
                   Z * np.ones([5, 1]) == 1]

    prob = cvx.Problem(cvx.Minimize(np.ones([1, N]) * (Z @ (s ** 2)) / N), constraints)
    prob.solve(solver=solver, verbose=False)

    x = np.matmul(Z.value, s)
    xc = np.zeros(len(x))

    for k in range(len(x)):
        idx = np.where(np.abs(np.array(s)-x[k])==np.min(np.abs(np.array(s)-x[k])))[0][0]
        xc[k] = s[idx]

    if prob.status=='infeasible':
        print('A multilevel PWM with the given constraints does not exist!')
        return -1
    elif(prob.status=='optimal_inaccurate'):
        print('Solution is inaccurate. Try another solver!')
    else:
        print('An optimal solution was found!')

    angles = 360 * (np.where(np.abs(np.diff(xc)) > 0)[0] + 1) / N
    y = np.matmul(sp.linalg.dft(N), xc) / N

    if(plots=='ON'):
        plt.figure()
        plt.title('The Multievel PWM Waveform', fontsize=30)
        plt.plot(np.linspace(0, N - 1, N), xc)

        plt.figure()
        plt.title('The Fourier Spectrum', fontsize=30)
        plt.stem(np.linspace(0,N-1,N), np.abs(y))

    diff_norm_percentage = 100*(np.linalg.norm(2*Fc@xc/N-cosinevals) + np.linalg.norm(2*Fs@xc/N-sinevals))/(np.sum(np.abs(cosinevals)) + np.sum(np.abs(sinevals)))

    return xc, angles, diff_norm_percentage


import numpy as np
import csv
import sys
import matplotlib.pyplot as plt
from scipy.special import erf

A = -np.pi           # random offset lower bound
B =  np.pi           # random offset higher bound
SEED = 112358      # numpy.random seed for reproducibility

def unif(a,b):
    return a + np.random.rand()*(b-a)

def random_jump_location():
    return unif(A,B),unif(A,B)

def initial_condition(x,y):
    # return np.sin(x)*np.cos(y)
    return -np.cos(x)*np.sin(y)

def exact_avg(x,y):
    # return ( np.cos(x-B) - np.cos(x-A) )*( np.sin(y-A) - np.sin(y-B) )/(B-A)/(B-A)
    return -( np.cos(y-B) - np.cos(y-A) )*( np.sin(x-A) - np.sin(x-B) )/(B-A)/(B-A)

def exact_avg_square(x,y):
    # return (np.sin(2*(x-B))-np.sin(2*(x-A)) +2*(B-A) )*\
    #         (np.sin(2*(y-A))-np.sin(2*(y-B)) +2*(B-A))/(B-A)/(B-A)/16
    return (np.sin(2*(y-B))-np.sin(2*(y-A)) +2*(B-A) )*\
            (np.sin(2*(x-A))-np.sin(2*(x-B)) +2*(B-A))/(B-A)/(B-A)/16

def exact_var(x,y):
    return exact_avg_square(x,y) - np.square(exact_avg(x,y))

def make_meanvar(N,M):
    dx = 2*np.pi/N
    x,y = np.mgrid[0:2*np.pi:dx, 0:2*np.pi:dx]
    average = np.zeros((N,N))
    M2 = np.zeros((N,N))
    delta = np.zeros((N,N))
    for m in range(M):
        X,Y = random_jump_location()
        U = initial_condition(x-X,y-Y)
        delta = U - average
        average += delta/(m+1)
        delta2 = U - average
        M2 += delta*delta2
    return (average, M2/(M-1))

M = 1024
N = 64

average,var = make_meanvar(N,M)

dx = 2*np.pi/N
x,y = np.mgrid[0:2*np.pi:dx, 0:2*np.pi:dx]
plt.subplot(321)
plt.title("s mean")
plt.contourf(x,y,average)
plt.colorbar()
plt.subplot(323)
plt.title("e mean")
plt.contourf(x,y,exact_avg(x,y))
plt.colorbar()
plt.subplot(325)
plt.contourf(x,y,average- exact_avg(x,y))
plt.colorbar()

plt.subplot(322)
plt.title("s var")
plt.contourf(x,y,var)
plt.colorbar()
plt.subplot(324)
plt.title("e var")
plt.contourf(x,y,exact_var(x,y))
plt.colorbar()
plt.subplot(326)
plt.contourf(x,y,var- exact_var(x,y))
plt.colorbar()

plt.show()

plt.close('all')
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x,y,average)
plt.show()

plt.close('all')
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
ax.plot_surface(x,y,var)
plt.show()


import numpy as np

def summary(data):
    """
    Get summary statistics for the input image. Summary statistics included are:
    count, minimum, 25th percentiel, mean, median, 75th percentile, maximum, standard deviation.

    Parameters
    ----------
    image : Path
        Path to tiff image.
    shapefile : Path
        Path to shapefile.

    Returns
    -------
    summary_stats : dictionary
        Dictionary containing the year and day of acquisiton and summary statistics for the image.

    """

    count = np.count_nonzero(~np.isnan(data))
    minimum = np.nanmin(data)
    p25 = np.nanpercentile(data, 25)
    mean = np.nanmean(data)
    median = np.nanmedian(data)
    p75 = np.nanpercentile(data, 75)
    maximum = np.nanmax(data)
    stdev = np.nanstd(data)

    summary_stats = {'count': count,
                     'minimum': minimum,
                     '25th_percentile': p25,
                     'mean': mean,
                     'median': median,
                     '75th_percentile': p75,
                     'maximum': maximum,
                     'stdev': stdev}

    return summary_stats