import random
import math

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

__author__ = '__Girish_Hegde__'

class Sampler:
    def __init__(self, radius=1, center=(0, 0), method='rejection_sample'):
        self.radius = radius
        self.r2 = radius**2
        self.cx, self.cy = center
        self._sample_unit_circle = getattr(self, method)

    def sample(self, n=1):
        points = []
        for i in range(n):
            x, y = self._sample_unit_circle()
            # translate (x, y)
            x, y = self.cx + x*self.radius, self.cy + y*self.radius
            points.append((x, y))
        return np.array(points)

    def rejection_sample(self):
        """Rejection Sampling

        1. point = uniform()
        2. if point satisfies required equation: return point
        3. else: repeat 1, 2, 3

        For circle:
            eqution: x2 + y2 = r2
            prob(success) = area(circle)/area(square)
                          = (pi.r2)/(4r2)
                          = pi/4 = 78.5%
            average trials per sample = 4/pi = 1.28

        """
        x = (random.random() - 0.5)*2
        y = (random.random() - 0.5)*2
        # check if (x, y) inside unit circle
        while not x*x + y*y < 1:
            # uniform(-1, 1)
            x = (random.random() - 0.5)*2
            y = (random.random() - 0.5)*2
        return (x, y)

    def ITS(self):
        """Inverse Transform Samples

        1. Get pdf
        2. cdf = integration(pdf)
        3. Get inverse cdf
        3. sample = inverse_cdf(uniform sample)

        For circle:
            polar coords -> (r, theta)
            theta = uniform(0, 2.pi)
            radius sampling:
                r != uniform(0, 1) <- as radius increases perimeter increases
                hence uniform sampling -> samples(large radius) < samples(small radius)
                -----------------------------------------------------------------------
                samples = k.perimeter = k.2.pi.r
                pdf = mr
                integral(pdf) = prob(samples space) = 1
                mr2/2 = 1
                m = 2
                pdf = 2r
                cdf = integral(pdf) = r2
                r = sqrt(uniform(0, 1)) <- inverse transform

        """
        # uniform_sample(0, 2.pi)
        theta = random.random()*2*math.pi
        # r = sqrt(uniform(0, 1)) <- inverse transform
        radius = math.sqrt(random.random())
        # polar -> cartesian
        x = radius*math.cos(theta)
        y = radius*math.sin(theta)
        return (x, y)

    def triangle_sample(self):
        """ Zero Area Triangle Sampling

        1. consider a parallellogram
        2. v1 = sample a vector along 1 side
        3. v2 = sample a vector along adjacent side
        4. point inside ||gm = v1 + v2
        5. point inside triangle = v1 + v2 if inside diagonal else reflect v1 + v2

        For circle:
            consider circle is made up of infinite triangles of area zero -> lines
            sample = uniform(0, 1) + uniform(0, 1)
            radius = sample  if sample < 1 else 2 - sample
            choose triangle -> theta = uniform(0, 2.pi)
            Note: pdf(of this method) == pdf(ITS method above) = 2r

        """
        # uniform_sample(0, 2.pi)
        theta = random.random()*2*math.pi
        radius = random.random() + random.random()
        if radius >= 1:
            radius = 2 - radius
        # polar -> cartesian
        x = radius*math.cos(theta)
        y = radius*math.sin(theta)
        return (x, y)

def main():
    r = 200
    # cx, cy = -200, 100
    cx, cy = 0, 0
    n = 10000
    clr = (0, 0, 255)

    hw = 3*(max(abs(cx), abs(cy)) + r)
    if hw%2 == 0:
        hw += 1
    coord_frame = np.zeros((hw, hw, 3),  dtype=np.uint8)
    coord_frame[hw//2, :] = 255
    coord_frame[:, hw//2] = 255
    cv2.circle(coord_frame, (hw//2  + cx, hw - (hw//2 +cy)), r, (255, 0, 0))

    sampler = Sampler(r, (cx, cy),  'ITS')
    # sampler = Sampler(r, (cx, cy),  'rejection_sample')
    # sampler = Sampler(r, (cx, cy),  'triangle_sample')

    samples = sampler.sample(n).astype(np.int32)
    for pt in  samples:
        coord_frame[hw - (hw//2 + pt[1]), hw//2 + pt[0]] = clr
        cv2.imshow("circle sampling", coord_frame)
        k = cv2.waitKey(1)
        if k == ord('q'):
            break

    # coord_frame[hw - 1 - (hw//2 + samples[:, 1]), hw//2 + samples[:, 0]] = [0, 0, 255]
    # cv2.imshow("circle sampling", coord_frame)
    # cv2.waitKey()
    # cv2.destroyAllWindows()

if __name__ == '__main__':
    main()


import numpy as np
import math
import matplotlib.pyplot as plt
from pylab import *
mpl.rcParams['font.sans-serif'] = ['SimHei']

def loadData(fileName):
    x = [[], [], [], [], [], [], [], [], [], []]
    inFile = open(fileName, 'r')  # 以只读方式打开fileName文件
    for c in inFile.readlines():
        c_array=c.split(',')
        for i in range(10):
            x[i].append(float(c_array[i]))
    return x

a=loadData("magic04.txt")
x = np.array(a) #将原始数据化成矩阵
z = np.array(x.mean(axis=1)).reshape(10, 1) #计算原数据的均值向量（10*1）
p=x-z   #计算中心化后的矩阵
def inner():
    #使用内积计算协方差矩阵，存储在y中
    y=np.dot(x-z,(x-z).T)/19019
    return y

def outer():
    #使用外积计算协方差矩阵，最后存储在d中，为10*10矩阵
    d=np.zeros(shape=(10,10))
    for c in range(19020):
        b=p.T[c]
        d=d+np.outer(b,b)
    d=d/19019
    return d

def cosineAndCorrelation():
    v=np.dot(p[0],p[1])
    v0=np.dot(p[0],p[0])
    v1=np.dot(p[1],p[1])
    cosine=v/math.sqrt(v0*v1)
    #相关系数和标准化后的1,2属性的角度余弦值相等
    plt.scatter(x[0], x[1],s=0.1)
    plt.xlabel("第一属性")
    plt.ylabel("第二属性")
    plt.show()
    return cosine

def normfun(x0, mu, sigma):
    # normfun正态分布函数，mu: 均值，sigma:标准差，pdf:概率密度函数，np.exp():概率密度函数公式
    pdf = np.exp(-((x0 - mu)**2) / (2* sigma**2)) / (sigma * np.sqrt(2*np.pi))
    return pdf

def plotNormalDis():
    # 计算属性1的均值与标准差
    mean=np.mean(x[0])
    std=np.std(x[0])
    # x的范围为，以0.01为单位,需x根据范围调试
    a0 = np.arange(0, 335, 0.01)
    b = normfun(a0, mean, std)
    plt.plot(a0,b, color='g',linewidth = 1)
    #数据，数组，颜色，颜色深浅，组宽，显示频率
    plt.hist(x[0], bins=300,color='r',alpha=0.5,rwidth= 0.9,density=True)
    plt.xlabel("第一属性值")
    plt.ylabel("概率")
    plt.show()

def variance():
    vari=[0 for t in range(10)]
    for i in range(10):
        vari[i]=np.var(a[i])
    print("方差最大的属性是：",argmax(vari)+1,"，方差最小的属性是：",argmin(vari)+1)

def covariance():
    li=[]
    for m in range (10):
        for n in range (m+1,10):
            d=np.dot(p[m],p[n])/19019
            li.append(d)
    print("协方差最大的两组属性是：0和",argmax(li)+1,"；协方差最小的两组属性是：0和",argmin(li)+1)

def main():
    print('数据均值向量：\n',z)
    print('内积算协方差矩阵：\n',inner())
    print('外积算协方差矩阵：\n',outer())
    co=cosineAndCorrelation()
    print('属性1、2的相关性为：',co)
    plotNormalDis()
    variance()
    covariance()

if __name__ == '__main__':
    main()


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

import numpy as np

def rotate_vector(vector, rot_mat):

    "rotating a vector by a rotation matrix"
    rotated_vec = np.dot(vector,rot_mat)
    return rotated_vec

def rotation_matrix_3D(vector, theta):

    """counterclockwise rotation about a unit vector by theta radians using
    Euler-Rodrigues formula: https://en.wikipedia.org/wiki/Euler-Rodrigues_formula"""

    a=np.cos(theta/2.0)
    b,c,d=-vector*np.sin(theta/2.0)
    aa,bb,cc,dd=a**2, b**2, c**2, d**2
    bc,ad,ac,ab,bd,cd=b*c, a*d, a*c, a*b, b*d, c*d

    rot_mat=np.array([[aa+bb-cc-dd, 2*(bc+ad), 2*(bd-ac)],
                         [2*(bc-ad), aa+cc-bb-dd, 2*(cd+ab)],
                         [2*(bd+ac), 2*(cd-ab), aa+dd-bb-cc]])
    return rot_mat

def unit_normal_vector(vec1, vec2):

    n = np.cross(vec1, vec2)
    if np.array_equal(n, np.array([0, 0, 0])):
        n = vec1

    s = max(np.sqrt(np.dot(n,n)), 1e-5)
    n = n/s
    return n

def angle(vec1, vec2):

    theta=np.arccos(np.dot(vec1,vec2) / (np.sqrt(np.dot(vec1,vec1)) * np.sqrt(np.dot(vec2, vec2))))
    return theta


from math import tanh, log, log10, exp
import numpy as np

class Var:
    """
    A variable which holds a number and enables gradient computations.

    Adapted from Rasmus Berg Palm `repository <https://github.com/rasmusbergpalm/nanograd>`_.

    Parameters
    ----------
    val : float or int
        The actual value of the number.
    parents : list, optional
        List where each item has the following structure (:class:`Var`, gradient)

    Attributes
    ----------
    grad : float or int
        Partial derivative of this variable with respect to the variable from which the bakcward method was called.

    Raises
    ------
    AssertionError
        If the val is not float or int.

    """

    def __init__(self, val, parents=None):
        assert type(val) in {float, int}
        if parents is None:
            parents = []
        self.v = val
        self.parents = parents
        self.grad = 0.0

    def backprop(self, bp):
        self.grad += bp
        for parent, grad in self.parents:
            parent.backprop(grad * bp)

    def backward(self):
        """
        Compute gradient of this Var with respect to all its parents.
        """
        self.backprop(1.0)

    def __add__(self, other):
        return Var(self.v + other.v, [(self, 1.0), (other, 1.0)])

    def __mul__(self, other):
        return Var(self.v * other.v, [(self, other.v), (other, self.v)])

    def __pow__(self, power):
        assert type(power) in {float, int}, "power must be float or int"
        return Var(self.v ** power, [(self, power * self.v ** (power - 1))])

    def __neg__(self):
        return Var(-1.0) * self

    def __sub__(self, other):
        return self + (-other)

    def __truediv__(self, other):
        return self * other ** -1

    def __lt__(self, other):
        return self.v < other.v

    def tanh(self):
        """Peform Tanh activation function.
        """
        return Var(tanh(self.v), [(self, 1 - tanh(self.v) ** 2)])

    def relu(self):
        """Peform Relu activation function.

        Relu is defined as follows:

        .. math::

            relu(x) = max(0, x)

        """
        return Var(self.v if self.v > 0.0 else 0.0, [(self, 1.0 if self.v > 0.0 else 0.0)])

    def log(self):
        """Peform log function. (base 10)
        """
        return Var(log10(self.v), [(self, 1 / self.v * log(10))])

    def exp(self):
        """Peform exp function.
        """
        return Var(exp(self.v), [(self, exp(self.v))])

    def __float__(self):
        return self.v

    def __int__(self):
        return int(self.v)

    def __repr__(self):
        return f'{self.v}'


"""
ALS methode for CP decomposition
"""
import numpy as np
import tensorly as tl
import time
import copy
from src._base import svd_init_fac,err

def err_fast(norm_tensor,A,V,W):
  """
    fast data fitting error calculation of als
    Parameters
    ----------
    norm_tensor : float
        norm of the tensor
    A : matrix
        factor matrix
    V : matrix
        matrix V defined as in als
    W : matrix
        matrix W defined as in als

    Returns
    -------
    float
    data fitting error

  """
  res=sum(sum(V*(np.transpose(A).dot(A))))
  res=res-2*sum(sum(W*A))
  return(np.sqrt(norm_tensor**2+res))

def als(tensor,rank,factors=None,it_max=100,tol=1e-7,list_factors=False,error_fast=True,time_rec=False):
  """
    ALS methode of CP decomposition

    Parameters
    ----------
    tensor : tensor
    rank : int
    factors : list of matrices, optional
        an initial factor matrices. The default is None.
    it_max : int, optional
        maximal number of iteration. The default is 100.
    tol : float, optional
        error tolerance. The default is 1e-7.
    list_factors : boolean, optional
        If true, then return factor matrices of each iteration. The default is False.
    error_fast : boolean, optional
        If true, use err_fast to compute data fitting error, otherwise, use err. The default is True.
    time_rec : boolean, optional
        If true, return computation time of each iteration. The default is False.

    Returns
    -------
    the CP decomposition, number of iteration and termination criterion.
    list_fac and list_time are optional.
  """
  N=tl.ndim(tensor) # order of tensor
  norm_tensor=tl.norm(tensor) # norm of tensor
  if time_rec == True : list_time=[]
  if list_factors==True : list_fac=[] # list of factor matrices

  if (factors==None): factors=svd_init_fac(tensor,rank)

  weights=None
  it=0
  if list_factors==True : list_fac.append(copy.deepcopy(factors))
  error=[err(tensor,weights,factors)/norm_tensor]
  while (error[len(error)-1]>tol and it<it_max):
    if time_rec == True : tic=time.time()
    for n in range(N):
      V=np.ones((rank,rank))
      for i in range(len(factors)):
        if i != n : V=V*tl.dot(tl.transpose(factors[i]),factors[i])
      W=tl.cp_tensor.unfolding_dot_khatri_rao(tensor, (None,factors), n)
      factors[n]= tl.transpose(tl.solve(tl.transpose(V),tl.transpose(W)))
    if list_factors==True : list_fac.append(copy.deepcopy(factors))
    it=it+1
    if (error_fast==False) : error.append(err(tensor,weights,factors)/norm_tensor)
    else : error.append(err_fast(norm_tensor,factors[N-1],V,W)/norm_tensor)
    if time_rec == True :
      toc=time.time()
      list_time.append(toc-tic)
  # weights,factors=tl.cp_tensor.cp_normalize((None,factors))
  if list_factors==True and time_rec==True: return(weights,factors,it,error,list_fac,list_time)
  if time_rec==True : return(weights,factors,it,error,list_time)
  if list_factors==True : return(weights,factors,it,error,list_fac)
  return(weights,factors,it,error)


import numpy as np
import matplotlib.pyplot as pl
pl.style.use(['science', 'no-latex'])

def mcpi(N=1000):
    points = np.random.uniform(0,1,(2,N))
    pointsInsideIndex = np.where(np.sum(np.square(points), axis=0) <= 1)[0]
    piEstimated = pointsInsideIndex.size / N * 4.0
    return points, pointsInsideIndex, piEstimated, (piEstimated-np.pi)/np.pi

def main():
    N = 30000
    points, pointsInsideIndex, piEstimated, piError = mcpi(N)
    while (abs(piError*100.0) > 0.02):
        points, pointsInsideIndex, piEstimated, piError = mcpi(N)
    print(piEstimated)
    fig, ax = pl.subplots(figsize=[5,5], dpi=300)
    ax.set_xlim([0,1])
    ax.set_ylim([0,1])
    ax.grid()
    ax.set_title(r"Point number = %d, $\pi_e \approx$ %.4f, error = %.3f%%" %(N, piEstimated, piError*100.0), fontsize=10)
    ax.plot(points[0][pointsInsideIndex], points[1][pointsInsideIndex], 'r.', markersize=0.5)
    ax.plot(np.delete(points[0], pointsInsideIndex), np.delete(points[1], pointsInsideIndex), 'b.', markersize=0.4)
#     fig.show()
    fig.savefig("piEstimation.png", bbox_inches='tight', pad_inches=0, dpi=300)
    fig.savefig("piEstimation.pdf", bbox_inches='tight', pad_inches=0, dpi=300)
    fig.savefig("piEstimation.eps", bbox_inches='tight', pad_inches=0, dpi=300)
    pl.close()

if __name__ == "__main__":
    main()


import math
import numpy as np
from scipy import ndimage

def gauss2d(sigma, fsize):
    """
  Args:
    sigma: width of the Gaussian filter
    fsize: dimensions of the filter

  Returns:
    g: *normalized* Gaussian filter
  """

    #
    # You code here
    #

    # l2-cameras_filtering Seite 60

    m, n = fsize[0], fsize[1]
    # Just make matrixes
    x = np.arange(-m / 2 + 0.5, m / 2)
    y = np.arange(-n / 2 + 0.5, n / 2)
    # https://numpy.org/doc/stable/reference/generated/numpy.meshgrid.html
    # Expand rank
    X, Y = np.meshgrid(x, y, sparse=True)
    g = np.exp(-(X ** 2 + Y ** 2) / (2 * sigma ** 2))
    # Normalization
    return g / np.sum(g)

def createfilters():
    """
  Returns:
    fx, fy: filters as described in the problem assignment
  """

    #
    # You code here
    #

    # l3-edges_pyramids Seite 33/35
    dx = np.expand_dims(np.array([0.5, 0, -0.5]), 0)
    gy = gauss2d(0.9, [1, 3])
    fx = gy * dx
    fy = fx.T
    return fx, fy

def filterimage(I, fx, fy):
    """ Filter the image with the filters fx, fy.
  You may use the ndimage.convolve scipy-function.

  Args:
    I: a (H,W) numpy array storing image data
    fx, fy: filters

  Returns:
    Ix, Iy: images filtered by fx and fy respectively
  """

    #
    # You code here
    #
    Ix = ndimage.convolve(I, fx)
    Iy = ndimage.convolve(I, fy)
    return Ix, Iy

def detectedges(Ix, Iy, thr):
    """ Detects edges by applying a threshold on the image gradient magnitude.

  Args:
    Ix, Iy: filtered images
    thr: the threshold value

  Returns:
    edges: (H,W) array that contains the magnitude of the image gradient at edges and 0 otherwise
  """

    #
    # You code here
    #

    # l3-edges_pyramids Seite 47
    edges = np.sqrt(Ix ** 2 + Iy ** 2)
    edges[edges < thr] = 0
    return edges

def nonmaxsupp(edges, Ix, Iy):
    """ Performs non-maximum suppression on an edge map.

  Args:
    edges: edge map containing the magnitude of the image gradient at edges and 0 otherwise
    Ix, Iy: filtered images

  Returns:
    edges2: edge map where non-maximum edges are suppressed
  """

    # Store result here
    edges2 = np.ones_like(edges)

    # Expand by one pixel
    padedges = np.pad(edges, 1)

    # Edge orientation in [-90, 90]
    orientation = np.arctan(Iy / (Ix + 1e-24))

    # Store indices
    r = edges.shape[0]
    c = edges.shape[1]

    # Suppress non-maxima in 4 directions
    pi8 = math.pi / 8

    # handle top-to-bottom edges: theta in [-90, -67.5] or (67.5, 90]

    # You code here
    is_theta = np.logical_or((orientation <= -3 * pi8), (orientation > 3 * pi8))
    is_nonmax = (padedges[1:r + 1, 1:c + 1] < np.maximum(padedges[0:r, 1:c + 1], padedges[2:r + 2, 1:c + 1]))
    edges2[np.logical_and(is_theta, is_nonmax)] = 0

    # handle left-to-right edges: theta in (-22.5, 22.5]

    # You code here
    is_theta = np.logical_or((orientation > -pi8), (orientation <= pi8))
    is_nonmax = (padedges[1:r + 1, 1:c + 1] < np.maximum(padedges[1:r + 1, 0:c], padedges[1:r + 1, 2:c + 2]))
    edges2[np.logical_and(is_theta, is_nonmax)] = 0

    # handle bottomleft-to-topright edges: theta in (22.5, 67.5]

    # Your code here
    is_theta = np.logical_or((orientation > pi8), (orientation <= 3 * pi8))
    is_nonmax = (padedges[1:r + 1, 1:c + 1] < np.maximum(padedges[0:r, 0:c], padedges[2:r + 2, 2:c + 2]))
    edges2[np.logical_and(is_theta, is_nonmax)] = 0

    # handle topleft-to-bottomright edges: theta in [-67.5, -22.5]

    # Your code here
    is_theta = np.logical_or((orientation > -3 * pi8), (orientation <= -pi8))
    is_nonmax = (padedges[1:r + 1, 1:c + 1] < np.maximum(padedges[2:r + 2, 0:c], padedges[0:r, 2:c + 2]))
    edges2[np.logical_and(is_theta, is_nonmax)] = 0

    return edges2 * edges


"""
Simple Metropolis Hastings sampler taken from Hogg and Foreman-MacKey(2018):

  From Problem 2:
  Sample in a single parameter x and give the sampler
  as its density function p(x) a Gaussian density with
  mean 2 and variance 2. Make the proposal distribution
  q (x'∣x ) a Gaussian pdf for x′ with mean x and
  variance 1. Initialize the sampler with x = 0 and run
  the sampler for more than 10000 steps. Plot the results
  as a histogram with the true density overplotted sensibly.

  Redo Problem 2, but now with an input density that
  is uniform on 3<x<7 and zero everywhere else. The plot
  should look like Figure 2. What change did you have to make
  to the initialization, and why?

"""

import probayes as pb
import numpy as np
import scipy.stats
from pylab import *; ion()

n_steps = 12288

def q(**kwds):
  x, xprime = kwds['x'], kwds["x'"]
  return scipy.stats.norm.pdf(xprime, loc=x, scale=1.)

x = pb.RV('x', vtype=float, vset=(-np.inf, np.inf))
process = pb.SP(x)
process.set_prob(scipy.stats.uniform.pdf, loc=3., scale=4.,
                 order={'x': 0})
process.set_tran(q)
lambda_delta = lambda : process.Delta(x=scipy.stats.norm.rvs(loc=0., scale=1.))
process.set_delta(lambda_delta)
process.set_scores('hastings')
process.set_update('metropolis')
sampler = process.sampler({'x': 5.}, stop=n_steps)
samples = [sample for sample in sampler]
summary = process(samples)
xvals = summary.v['x']

figure()
xbins = np.linspace(np.min(xvals), np.max(xvals), 128)
xhist, _ = np.histogram(xvals, xbins)
xprop = xhist / (n_steps * (xbins[1]-xbins[0]))
step(xbins[:-1], xprop, 'b')
norm_x = scipy.stats.uniform.pdf(xbins, loc=3., scale=4.)
plot(xbins, norm_x, 'r')


import matplotlib.pyplot as plt
import numpy as np

def __rescaled_length(series, offset, scale):
    n_full = len(series)
    diffs = np.diff(series[offset::scale])
    n_rescaled = len(diffs) + 1
    sum_term = np.sum(np.abs(diffs))
    return (n_full - 1) / (n_rescaled * (scale ** 2)) * sum_term

def __mean_length(series, scale):
    return np.mean(
        [__rescaled_length(series, offset, scale) for offset in np.arange(0, scale)]
    )

def higuchi_dimension(
    series: list[float], scales: list[int], plot_curves: bool = False
):
    """Calculate Higuchi dimension.

    Args:
        series      - 1D list of real numbers, whose Higuchi dimension needs to
                      be estimated.
        scales      - 1D list of integer numbers describing the scales at which
                      the series ought to be analysed.
        plot_curves - whether to plot the length vs 1/scale plot. (optional)

    Returns:
        An estimate of Higuchi dimension. Optionally one can choose to plot
        the curves, from which the Higuchi dimension is estimated.
    """
    log_lengths = np.log([__mean_length(series, scale) for scale in scales])
    log_scales = -np.log(scales)

    coeffs = np.polyfit(log_scales, log_lengths, 1)

    if plot_curves:
        plt.figure()
        plt.xlabel("log(1/scale)")
        plt.ylabel("log(length)")
        plt.plot(log_scales, log_lengths, "ro")
        plt.plot(log_scales, np.polyval(coeffs, log_scales), "k-")
        plt.show()

    return coeffs[0]


# -*- coding: utf-8 -*-
"""
Created on Mon Nov 26 15:31:57 2018
构成比的卡方检验
Chi-square test of independence of variables in a contingency table.
@author: lenovo
"""
from scipy.stats import chi2_contingency
from scipy.stats import chi2
import numpy as np

def lc_chi2(data):
    # data=np.array([[10,20],[10,30],[20,400]])
    results = chi2_contingency(data)
    chi2value = results[0]
    pvalue = results[1]
    return (chi2value, pvalue)

def lc_chisqure(obs, tt):
    """
    obs: observed frequence
    tt: total number of each group
    NOTE. Make sure the number are np.array
    The results is in line with SPSS
    """
    tt = np.array(tt)
    obs1 = obs
    obs1 = np.array(obs1)
    obs2 = tt - obs1
    obs_all = np.vstack([obs1, obs2]).T
    n_row = np.shape(obs_all)[0]
    n_col = np.shape(obs_all)[1]
    df = (n_row - 1) * (n_col) / 2  # free degree

    frq1 = np.sum(obs1) / np.sum(tt)
    frq2 = np.sum(obs2) / np.sum(tt)
    f_exp1 = tt * frq1
    f_exp2 = tt * frq2
    f_exp = np.vstack([f_exp1, f_exp2]).T

    chisqurevalue = np.sum(((obs_all - f_exp)**2) / f_exp)
    p = (1 - chi2.cdf(chisqurevalue, df=df))
    return chisqurevalue, p

if __name__ == "__main__":
    tt = [120, 81 - 20]
    obs = [31, 67 - 20]
    print(lc_chisqure(obs, tt))


import math
import time

import torch
import numpy as np
from d2l import torch as d2l

def normal(x, mu, sigma):
    """
    the function of Normal Distribution, see ch3.1.3
    """
    p = 1 / math.sqrt(2 * math.pi * sigma ** 2)
    return p * np.exp(-0.5 / sigma ** 2 * (x - mu) ** 2)

class Timer:
    """
    Record multiple running times.
    """

    def __init__(self):
        self.times = []
        self.start()

    def start(self):
        """
        Start the timer.
        """
        self.tik = time.time()

    def stop(self):
        """
        Stop the timer and record the time in a list.
        """
        self.times.append(time.time() - self.tik)
        return self.times[-1]

    def avg(self):
        """
        Return the average time.
        """
        return sum(self.times) / len(self.times)

    def sum(self):
        """
        Return the sum of time.
        """
        return sum(self.times)

    def cumsum(self):
        """
        Return the accumulated time.
        """
        return np.array(self.times).cumsum().tolist()

if __name__ == "__main__":
    NUM = 10000
    a = torch.ones(NUM)
    b = torch.ones(NUM)
    c = torch.zeros(NUM)
    timer = Timer()
    for i in range(NUM):
        c[i] = a[i] + b[i]
    print(f"{timer.stop():.5f} sec")

    timer.start()
    d = a + b
    print(f"{timer.stop():.5f} sec")

    # Use numpy again for visualization
    x = np.arange(-7, 7, 0.01)

    # Mean and standard deviation pairs
    params = [(0, 1), (0, 2), (3, 1)]
    d2l.plot(
        x,
        [normal(x, mu, sigma) for mu, sigma in params],
        xlabel="x",
        ylabel="p(x)",
        figsize=(4.5, 2.5),
        legend=[f"mean {mu}, std {sigma}" for mu, sigma in params],
    )


"""
It was proposed by Christian Goldbach that every odd composite number can be written as the sum of a prime and twice a square.

9 = 7 + 2*1^2
15 = 7 + 2*2^2
21 = 3 + 2*3^2
25 = 7 + 2*3^2
27 = 19 + 2*2^2
33 = 31 + 2*1^2

It turns out that the conjecture was false.

What is the smallest odd composite that cannot be written as the sum of a prime and twice a square?
"""

import numpy as np

def generate_primes(n=1000):
	primes = [1]
	prime_flags = [0]*(n+1)
	for prime in xrange(2, n):
		if prime_flags[prime] == 0:
			primes.append(prime)
		else:
			continue
		mult = 2
		while mult*prime < n:
			prime_flags[mult*prime] = 1
			mult += 1
	return primes

primes = generate_primes(10000)

for i in xrange(1,10000, 2):
	print("%d: " % i),
	found = False
	for prime in primes:
		print("%d," % prime),
		if prime > i:
			break
		rez = ((i-prime)/2)**0.5
		if rez == int(rez):
			print("found %d+2*%d^2" % (prime, rez))
			found = True
			break
	if not found:
		print("\n%d couldn't be formulated as parts" % i)
		break


import numpy as np

# TODO: look for library that can represent rotation group else make one
# https: // en.wikipedia.org / wiki / Rotation_group_SO(3)

def rotate(xy, theta):
    # TODO: check shapes
    return rotation_matrix_2d(theta) @ xy

def rotate_degrees(xy, theta):
    return rotate(xy, np.radians(theta))

def rotation_matrix_2d(theta):
    """Rotation matrix"""
    cos = np.cos(theta)
    sin = np.sin(theta)
    return np.array([[cos, -sin],
                     [sin, cos]])

# aliases
rotation_matrix = rotation_matrix_2d
rotate_2d = rotate
rotate_degrees_2d = rotate_degrees

def rotation_matrix_3d(axis, theta):
    """
    Use Euler–Rodrigues formula to generate the 3x3 rotation matrix for
    rotation of `theta` radians about Cartesian direction vector `axis`.

    Any 3d rotation can be represented this way: see Euler's rotation theorem.
    These rotations have a group structure and can thus be composed to yield
    another rotation.

    see: https://en.wikipedia.org/wiki/Euler%E2%80%93Rodrigues_formula
    """

    axis = np.asarray(axis)
    axis /= np.sqrt((axis * axis).sum())

    # 'Rodriguez parameters'
    a = np.cos(theta / 2)
    b, c, d = -axis * np.sin(theta / 2)

    # Rotation matrix
    aa, ac, ad = a * (a, c, d)
    bb, bc, bd = b * (b, c, d)
    cc, cd = c * (c, d)
    dd = d * d
    return  np.array([
        [aa + bb - cc - dd, 2 * (bc - ad),     2 * (bd + ac)],
        [2 * (bc + ad),     aa + cc - bb - dd, 2 * (cd - a * b)],
        [2 * (bd - ac),     2 * (cd + a * b),  aa + dd - bb - cc]
    ])

def rotate_3d(data, axis, theta):  # TODO: rename rotate_3d
    """Rotate `data` about `axis` by `theta` radians"""
    # Cast array in shape for matrix
    # data = data.reshape(data.shape)
    # multiplication (shape will be (r,c,3,1) where X,Y,Z have shape (r,c))
    # TODO: deduce dimensionality from axis
    R = rotation_matrix_3d(axis, theta)  # 3x3 rotation matrix

    # Coordinate transform
    XR, YR, ZR = np.dot(R, data).squeeze()
    # (higher dimensional equivalent of Matrix multiplication by R on each
    # point x,y,z in X,Y,Z).  squeeze flattens singular dimensions
    return XR, YR, ZR

def rotate_degrees_3d(data, axis, theta):
    """Rotate `data` about `axis` by `theta` degrees"""
    return rotate_3d(data, axis, np.radians(theta))


"""
Functions dealing with correlation

This file is a part of BdPy.
"""

__all__ = ['corrcoef', 'corrmat']

import numpy as np
from numpy.matlib import repmat

def corrcoef(x, y, var='row'):
    """
    Returns correlation coefficients between `x` and `y`

    Parameters
    ----------
    x, y : array_like
        Matrix or vector
    var : str, 'row' or 'col'
        Specifying whether rows (default) or columns represent variables

    Returns
    -------
    r
        Correlation coefficients
    """

    # Convert vectors to arrays
    if x.ndim == 1:
        x = np.array([x])

    if y.ndim == 1:
        y = np.array([y])

    # Normalize x and y to row-var format
    if var == 'row':
        # 'rowvar=1' in np.corrcoef

        # Vertical vector --> horizontal
        if x.shape[1] == 1:
            x = x.T
        if y.shape[1] == 1:
            y = y.T
    elif var == 'col':
        # 'rowvar=0' in np.corrcoef

        # Horizontal vector --> vertical
        if x.shape[0] == 1:
            x = x.T
        if y.shape[0] == 1:
            y = y.T

        # Convert to rowvar=1
        x = x.T
        y = y.T
    else:
        raise ValueError('Unknown var parameter specified')

    # Match size of x and y
    if x.shape[0] == 1 and y.shape[0] != 1:
        x = repmat(x, y.shape[0], 1)

    elif x.shape[0] != 1 and y.shape[0] == 1:
        y = repmat(y, x.shape[0], 1)

    # Check size of normalized x and y
    if x.shape != y.shape:
        raise TypeError('Input matrixes size mismatch')

    # Get num variables
    nvar = x.shape[0]

    # Get correlation
    rmat = np.corrcoef(x, y, rowvar=1)
    r = np.diag(rmat[:nvar, nvar:])

    return r

def corrmat(x, y, var='row'):
    """
    Returns correlation matrix between `x` and `y`

    Parameters
    ----------
    x, y : array_like
        Matrix or vector
    var : str, 'row' or 'col'
        Specifying whether rows (default) or columns represent variables

    Returns
    -------
    rmat
        Correlation matrix
    """

    # Fix x and y to represent variables in each row
    if var == 'row':
        pass
    elif var == 'col':
        x = x.T
        y = y.T
    else:
        raise ValueError('Unknown var parameter specified')

    nobs = x.shape[1]

    # Subtract mean(a, axis=1) from a
    submean = lambda a: a - np.matrix(np.mean(a, axis=1)).T

    cmat = (np.dot(submean(x), submean(y).T) / (nobs - 1)) / np.dot(np.matrix(np.std(x, axis=1, ddof=1)).T, np.matrix(np.std(y, axis=1, ddof=1)))

    return np.array(cmat)


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

qe = 1.60217662e-19
me = 9.10938356e-31
B0 = 0.1

# RHS of the ODE problem, dy/dx = f(x,y)
def fun(t,y):
   vx = y[3]
   vy = y[4]
   vz = y[5]
   # Charge-to-mass ratio (q/m)
   qm = -qe/me
   # E-field [V/m]
   Ex = 0.0
   Ey = 0.0
   Ez = 0.0
   # B-field [T]
   Bx = 0.0
   By = 0.0
   Bz = B0
   # Newton-Lorentz equation (in Cartesian Coords)
   ax = qm * Ex + qm*( Bz*vy - By*vz )
   ay = qm * Ey + qm*( Bx*vz - Bz*vx )
   az = qm * Ez + qm*( By*vx - Bx*vy )
   ydot = np.array(( vx, vy, vz, ax, ay, az ))
   return ydot

def main():

   # Initial velocity [m/s]
   vy0 = 1.0e6
   # Larmor pulsation [rad/s]
   w_L = qe/me * B0
   # Larmor period [s]
   tau_L = 2.0*np.pi/w_L
   # Larmor radius [m]
   r_L = vy0/w_L

   # Initial conditions
   y0 = np.array(( r_L, 0.0, 0.0, 0.0, vy0, 0.0 ))

   # Euler Forward
   time_ef = np.linspace( 0.0, tau_L, 10 )
   y_ef = ode.euler( fun, time_ef, y0 )

   # Explicit Midpoint
   time_mp = np.linspace( 0.0, tau_L, 10 )
   y_mp = ode.midpoint( fun, time_mp, y0 )

   # Runge-Kutta 4
   time_rk = np.linspace( 0.0, tau_L, 10 )
   y_rk = ode.rk4( fun, time_rk, y0 )

   # Amplitude (orbit radius)
   r_ef = np.sqrt( y_ef[:,0]**2 + y_ef[:,1]**2 )
   r_mp = np.sqrt( y_mp[:,0]**2 + y_mp[:,1]**2 )
   r_rk = np.sqrt( y_rk[:,0]**2 + y_rk[:,1]**2 )

   # Plot 1 - Trajectory
   plt.figure(1)
   plt.plot( y_ef[:,0], y_ef[:,1], 'ro-', label='Euler-Forward (1st)' )
   plt.plot( y_mp[:,0], y_mp[:,1], 'go-', label='Explicit Mid-Point (2nd)' )
   plt.plot( y_rk[:,0], y_rk[:,1], 'bx-', label='Runge-Kutta (4th)' )
   plt.axis('equal')
   plt.xlabel('x [m]')
   plt.ylabel('y [m]')
   plt.title('One Larmor Gyration')
   plt.legend(loc=3)
   plt.savefig('ex03_ode_larmor_trajectory.png')

   # Plot 2 - Amplitude percent error
   plt.figure(2)
   plt.plot( time_ef/tau_L, ode.error_percent( r_L, r_ef), 'ro', label='Forward Euler (1st)' )
   plt.plot( time_mp/tau_L, ode.error_percent( r_L, r_mp), 'go', label='MidPoint (2nd)' )
   plt.plot( time_rk/tau_L, ode.error_percent( r_L, r_rk), 'bx', label='Runge-Kutta (4th)' )
   plt.xlabel('time / tau_Larmor')
   plt.ylabel('Percent Amplitude error [%]')
   plt.title('Percent Amplitude Error over 1 Larmor gyration ')
   plt.legend(loc=2)
   plt.savefig('ex03_ode_larmor_error.png')
   plt.show()

if __name__ == '__main__':
   main()


"""
Statistical metrics.
"""
import numpy

def union_mask(reference, predicted):
    def _get_mask(s):
        if numpy.ma.is_masked(s):
            return ~numpy.isfinite(s.filled(numpy.nan))
        return ~numpy.isfinite(s)
    return _get_mask(reference) + _get_mask(predicted)

def compute_error(reference, predicted):
    return predicted - reference

def compute_bias(reference, predicted):
    err = compute_error(reference, predicted)
    return numpy.mean(err)

def compute_rmse(reference, predicted):
    err = compute_error(reference, predicted)
    return numpy.sqrt(numpy.mean(err**2))

def compute_crmse(reference, predicted):
    err = compute_error(reference - numpy.mean(reference),
                        predicted - numpy.mean(predicted))
    return numpy.sqrt(numpy.mean(err**2))

def compute_stddev(reference):
    return numpy.std(reference, ddof=1)

def compute_corrcoef(reference, predicted):
    return numpy.corrcoef(predicted, reference)[0, 1]

def compute_mean_abs_error(reference, predicted):
    err = compute_error(reference, predicted)
    return numpy.mean(numpy.abs(err))

def compute_stat(kind, reference, predicted):
    op = {
        'bias': compute_bias,
        'rmse': compute_rmse,
        'crmse': compute_crmse,
        'stddev': compute_stddev,
        'corrcoef': compute_corrcoef,
        'mae': compute_mean_abs_error,
    }
    args = (reference, predicted)
    if kind == 'stddev':
        args = (predicted, )
    return op[kind](*args)

def _compute_all_stats(reference, predicted):
    op_list = ['bias', 'rmse', 'crmse', 'stddev', 'corrcoef', 'mae']
    return dict([(o, compute_stat(o, reference, predicted)) for o in op_list])

def compute_statistics(reference, predicted,
                       add_normalized=False, add_crmse_sign=False):

    r = numpy.copy(reference)
    p = numpy.copy(predicted)

    # keep only values that are valid in both sets
    mask = union_mask(r, p)
    r = r[~mask]
    p = p[~mask]

    ref_stats = _compute_all_stats(r, r)
    pre_stats = _compute_all_stats(r, p)

    if add_normalized:
        # Add normalized metrics in the same dict as "norm_<metric>"
        r, p = normalize_statistics(ref_stats, pre_stats,
                                    add_crmse_sign=add_crmse_sign)
        for k in r:
            ref_stats['norm_' + k] = r[k]
            pre_stats['norm_' + k] = p[k]

    return ref_stats, pre_stats

def normalize_statistics(ref_stats, pre_stats, add_crmse_sign=False):

    norm_metrics = ['bias', 'rmse', 'crmse', 'stddev', 'mae']

    def _make_norm_dict(d, scalar):
        new = dict(d)
        for key in norm_metrics:
            new[key] *= scalar
        return new

    scalar = 1.0 / ref_stats['stddev']
    nref_stats = _make_norm_dict(ref_stats, scalar)
    npre_stats = _make_norm_dict(pre_stats, scalar)

    if add_crmse_sign:
        crmse_sign = numpy.sign(pre_stats['stddev'] - ref_stats['stddev'])
        npre_stats['crmse'] *= crmse_sign

    return nref_stats, npre_stats


'''
Reference
Dabhade S A ,  Bewoor M S ,  Dabhade S A , et al. Face Recognition using Principle Component Analysis[J]. Core.kmi.open.ac.uk, 2013, ETCSIT(100):45 - 50.

> Perhaps, the simplest classification scheme is a nearest neighbor classifier in
> the image space. Under this scheme, an image in the test set is recognized
> (classified) by assigning to it the label of the closest point in the learning
> set, where distances are measured in the image space.
'''
import numpy as np

from settings import FACE_COUNT
from settings import IMAGE_COUNT_PER_FACE
from settings import TRAIN_COUNT_PER_FACE
from image_parser import read_an_image
from math_utils import get_dist

def get_center(face_num):
    center = read_an_image(face_num, 0)
    center_len = len(center)
    for i in range(1, TRAIN_COUNT_PER_FACE):
        im_i = read_an_image(face_num, i)
        assert center_len == len(im_i)
        for j in range(center_len):
            center[j] = (center[j]*i+im_i[j])/(i+1)
    return center

def get_centers():
    centers = [get_center(0)]
    for i in range(1, FACE_COUNT):
        centers = np.append(centers, [get_center(i)], axis=0)
    return centers

def recognize_face(face_num, image_num_of_this_face, centers):
    im = read_an_image(face_num, image_num_of_this_face)
    face_recognized = face_num
    key_dist = get_dist(im, centers[face_num])
    for i in range(FACE_COUNT):
        if i == face_num:
            continue
        dist = get_dist(im, centers[i])
        if dist < key_dist:
            face_recognized = i
    return face_recognized

if __name__ == '__main__':
    print('###### Minimum Distance Face Recognition ######')
    print('The face recognition results of each person are listed in the following format:')
    print('Face Number | Image Number | Face Recognized | Right or Wrong (T/F)\n...\nError Rate\n')
    centers = get_centers()
    error_count = 0
    for i in range(FACE_COUNT):
        error_count_i = 0
        for j in range(TRAIN_COUNT_PER_FACE, IMAGE_COUNT_PER_FACE):
            face_recognized = recognize_face(i, j, centers)
            t_or_f = 'T'
            if face_recognized != i:
                t_or_f = 'F'
                error_count_i += 1
            print(f'{i+1}\t{j+1}\t{face_recognized+1}\t{t_or_f}')
        error_count += error_count_i
        print(f'{error_count_i} / {IMAGE_COUNT_PER_FACE-TRAIN_COUNT_PER_FACE} = {error_count_i/(IMAGE_COUNT_PER_FACE-TRAIN_COUNT_PER_FACE)}\n')
    print('Total Error Rate:')
    print(f'{error_count} / {(IMAGE_COUNT_PER_FACE-TRAIN_COUNT_PER_FACE)*FACE_COUNT} = {error_count/((IMAGE_COUNT_PER_FACE-TRAIN_COUNT_PER_FACE)*FACE_COUNT)}')


# Copyright 2017 Battelle Energy Alliance, LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# takes input parameters x,y
# returns value in "ans"
# elliptic paraboloid function with a single global minimum
# optimal minimum at f(-0.5, 0.5) = 0
# parameter range is -2 <= x <= 2, -2 <= y <= 2
import numpy as np

def run(raven, Inputs):
  """
    External model to evaluates 10*(x+0.5)**2 + 10*(y-0.5)**2.
    @ In, raven, object, container from RAVEN to store members on
    @ In, Input, dict, dictionary containing inputs from RAVEN
    @ Out, None
  """
  x = raven.x
  y = raven.y
  raven.ans = main(x, y)

def main(x, y):
  z = 10*(x+0.5)**2 + 10*(y-0.5)**2
  return z


import numpy as np
import quantecon as qe
import scipy.linalg as la

## Parameters

class ConsumptionProblem:
    """
    The data for a consumption problem, including some default values.
    """

    def __init__(self,
                 beta = .96,
                 y = [2, 1.5],
                 b0 = 3,
                 P = np.asarray([[.8, .2],
                                 [.4, .6]])):
        """

        Parameters
        ----------

        beta : discount factor
        P    : 2x2 transition matrix
        y    : list containing the two income levels
        b0   : debt in period 0 (= state_1 debt level)

        """
        self.beta = beta
        self.y = y
        self.b0 = b0
        self.P = P

def consumption_complete(cp):
    """
    Computes endogenous values for the complete market case.

    Parameters
    ----------

    cp : instance of ConsumptionProblem

    Returns
    -------

        c_bar : constant consumption
        b1    : rolled over b0
        b2    : debt in state_2

    associated with the price system

        Q = beta * P

    """
    beta, P, y, b0 = cp.beta, cp.P, cp.y, cp.b0  # Unpack

    y1, y2 = y          # extract income levels
    b1 = b0             # b1 is known to be equal to b0
    Q = beta * P        # assumed price system

    # Using equation (7) calculate b2
    b2 = (y2 - y1 - (Q[0, 0] - Q[1, 0] - 1) * b1)/(Q[0, 1] + 1 - Q[1, 1])

    # Using equation (5) calculae c_bar
    c_bar = y1 - b0 + Q[0, :] @ np.asarray([b1, b2])

    return c_bar, b1, b2

def consumption_incomplete(cp, N_simul=150):
    """
    Computes endogenous values for the incomplete market case.

    Parameters
    ----------

    cp : instance of ConsumptionProblem
    N_simul : int

    """

    beta, P, y, b0 = cp.beta, cp.P, cp.y, cp.b0  # Unpack
    # For the simulation define a quantecon MC class
    mc = qe.MarkovChain(P)

    # Useful variables
    y = np.asarray(y).reshape(2, 1)
    v = np.linalg.inv(np.eye(2) - beta * P) @ y

    # Simulat state path
    s_path = mc.simulate(N_simul, init = 0)

    # Store consumption and debt path
    b_path, c_path = np.ones(N_simul + 1), np.ones(N_simul)
    b_path[0] = b0

    # Optimal decisions from (12) and (13)
    db = ((1 - beta) * v - y) / beta

    for i, s in enumerate(s_path):
        c_path[i] = (1 - beta) * (v - b_path[i] * np.ones((2, 1)))[s, 0]
        b_path[i + 1] = b_path[i] + db[s, 0]

    return c_path, b_path[:-1], y[s_path], s_path


from scipy.linalg import solve
import cv2 as cv
import numpy as np
# pts1: np.array, pts2: np.array
def FindFundamentalMat(pts1: np.array, pts2: np.array):
    equations = []

    for i in range(pts1.shape[1]):
        u1 = pts1[0][i]
        v1 = pts1[1][i]
        u2 = pts2[0][i]
        v2 = pts2[1][i]
        equations.append([u1*u2, v1*u2, u2, u1*v2, v1*v2, v2, u1, v1, 1])

    equations = np.array(equations).T
    U,E,V = np.linalg.svd(equations)
    f = U[:, -1]
    print(np.round(np.matmul(f, equations), 3))
    F = f.reshape(3,3)

    U,E,V_T = np.linalg.svd(F, full_matrices=False)
    indx_min_sing_val = np.argmin(E)
    E[indx_min_sing_val] = 0.
    F_sing = np.round(np.matmul(np.matmul(U,np.diag(E)), V_T), 3)
    return F_sing

def findFundamnentalMatrix(points_list):
  A = []
  for points in points_list:
    x, y = points[0]
    u, v = points[1]
    A.append([u*x, u*y, u, x*v, y*v, v, x, y, 1])

  A = np.asarray(A, dtype=np.float)
  A = A.T

  u, s, vt =  np.linalg.svd(A)
  print("Minimal Singular Value: {}\n".format(s[-1]))
  x_arr = u[:, -1]
  h_i = np.reshape(x_arr, (3,3))
  print("Fundamental matrix:\n{}\n".format(h_i))
  test = x_arr.dot(A)
  test[test < 1e-10] = 0
  print("The next values must be ZERO: {}\n\n".format(test))
  return h_i

def GeneratePts(max_coord=30, num_points=8):
    R1 = np.identity(3)
    T1 = np.zeros(3).T.reshape(3,1)
    R2 = np.identity(3)
    T2 = np.array([10,0,0]).T.reshape(3,1)

    P1 = np.append(R1, T1, axis=1)
    P2 = np.append(R2, T2, axis=1)
    Q = np.append(np.array([np.random.rand(3)*10 for j in range(num_points)]),
                  np.ones(num_points).reshape(num_points,1), axis = 1)

    q1 = np.matmul(P1, Q.T)
    q2 = np.matmul(P2, Q.T)

    return q1/q1[2], q2/q2[2]

def main():
    pts = GeneratePts()
    print(FindFundamentalMat(pts[0], pts[1]))
main()


import numpy as np

def get_angle(v1, v2):
    '''
    Get angle from 2 vectors
    '''
    return np.arccos(np.dot(v1, v2) / (np.linalg.norm(v1) * np.linalg.norm(v2)))

def get_point_on_circle(angle, circle):
    x = circle.origin[0] + (circle.radius * np.cos(angle))
    y = circle.origin[1] + (circle.radius * np.sin(angle))
    return np.array([x, y])

def get_closest_point_on_circle(p, circle):
    '''
    Finding the closest x,y coordinates on circle, based on given point
    '''
    x_vec = np.array([1, 0])
    v = p - circle.origin
    angle = get_angle(v, x_vec)
    angle = angle if v[1] > 0 else -angle
    return get_point_on_circle(angle, circle)

if __name__ == '__main__':
    from pyrieef.geometry.workspace import Circle
    circle = Circle(origin=np.zeros(2), radius=1.0)
    p = np.array([0, 2])
    print(get_closest_point_on_circle(p, circle))
    p = np.array([-1, 1])
    print(get_closest_point_on_circle(p, circle))
    p = np.array([-1, 0])
    print(get_closest_point_on_circle(p, circle))
    p = np.array([0, -1])
    print(get_closest_point_on_circle(p, circle))
    p = np.array([1, -1])
    print(get_closest_point_on_circle(p, circle))
    p = np.array([1, 0])
    print(get_closest_point_on_circle(p, circle))


from math import gcd

from sage.all import GF
from sage.all import is_prime_power

def attack(y, m=None, a=None, c=None):
    """
    Recovers the parameters from a linear congruential generator.
    If no modulus is provided, attempts to recover the modulus from the outputs (may require many outputs).
    If no multiplier is provided, attempts to recover the multiplier from the outputs (requires at least 3 outputs).
    If no increment is provided, attempts to recover the increment from the outputs (requires at least 2 outputs).
    :param y: the sequential output values obtained from the LCG
    :param m: the modulus of the LCG (can be None)
    :param a: the multiplier of the LCG (can be None)
    :param c: the increment of the LCG (can be None)
    :return: a tuple containing the modulus, multiplier, and the increment
    """
    if m is None:
        assert len(y) >= 4, "At least 4 outputs are required to recover the modulus"
        for i in range(len(y) - 3):
            d0 = y[i + 1] - y[i]
            d1 = y[i + 2] - y[i + 1]
            d2 = y[i + 3] - y[i + 2]
            g = d2 * d0 - d1 * d1
            m = g if m is None else gcd(g, m)

        assert is_prime_power(m), "Modulus must be a prime power, try providing more outputs"

    gf = GF(m)
    if a is None:
        assert len(y) >= 3, "At least 3 outputs are required to recover the multiplier"
        x0 = gf(y[0])
        x1 = gf(y[1])
        x2 = gf(y[2])
        a = int((x2 - x1) / (x1 - x0))

    if c is None:
        assert len(y) >= 2, "At least 2 outputs are required to recover the multiplier"
        x0 = gf(y[0])
        x1 = gf(y[1])
        c = int(x1 - a * x0)

    return m, a, c


"""
This module implements
"""
import numpy as np

# TODO: implement, add descriptions etc
def haversine(start_lat,
          start_lon,
          end_lat,
          end_lon):
    """
        Calculate the great circle distance between two points
        on the earth (specified in decimal degrees).
        Computes distance in kms

            Parameters
        ----------
        start_lat: description
        start_lon: description

        Returns
        -------
        description
    """
    lat_1_rad, lon_1_rad = np.radians(start_lat), np.radians(start_lon)
    lat_2_rad, lon_2_rad = np.radians(end_lat), np.radians(end_lon)
    dlon = lon_2_rad - lon_1_rad
    dlat = lat_2_rad - lat_1_rad

    a = np.sin(dlat / 2.0) ** 2 + np.cos(lat_1_rad) * np.cos(lat_2_rad) * np.sin(dlon / 2.0) ** 2
    c = 2 * np.arcsin(np.sqrt(a))
    haversine_distance = 6371 * c

    return haversine_distance


import numpy as np
from scipy import linalg as la

class MarkovChain:

    def __init__(self, pi):
        if not np.allclose(np.sum(pi, axis=1), np.ones(pi.shape[0])):
            raise ValueError('Each row of the input matrix must sum to one.')
        self.Pi = pi

    def n_steps_transition(self, n):
        return la.matrix_power(self.Pi, n)

    @property
    def stationary_distribution(self):
        l, v = la.eig(self.Pi)
        vector = v[:, np.where(np.isclose(l, 1.))]
        return vector / np.sum(vector)

    def simulate(self, T, s0):
        """
        It simulates a Markov Chain for T periods given that the initial
        state is 's'. The parameter 's0' must be an integer between 0 and
        Pi.shape[0]-1
        """
        if T < 1:
            raise ValueError('The sample length T must be at least 1.')
        if not isinstance(s0, int):
            raise TypeError('Initial condition must be an index (integer).')
        if s0 < 0 or s0 > self.Pi.shape[0] - 1:
            raise ValueError('Initial condition must be a row index of Pi.')

        def draw_state(pdf):
            cdf = np.cumsum(pdf)
            u = np.random.uniform()
            return np.sum(u - cdf > 0)

        sample = np.zeros((T,), dtype=int)
        sample[0] = s0
        for t in range(1, T):
            sample[t] = draw_state(self.Pi[sample[t - 1], :])

        return sample


import numpy

def multivariate_gaussian(x_array, mean_mu, corr_sigma):
    """
    Computes the probability density function of the multivariate gaussian distribution.

    Parameters
    ----------
    x_array : array_like
        The dataset of shape (m x n). Where there are m examples of n-dimensions.

    mean_mu : array_like
        A vector of shape (n,) contains the means for each dimension (feature).

    corr_sigma : array_like
        Either a vector of shape (n,) containing the variances of independent features
        (i.e. it is the diagonal of the correlation matrix), or the full
        correlation matrix of shape (n x n) which can represent dependent features.

    Returns
    ------
    p : array_like
        A vector of shape (m,) which contains the computed probabilities at each of the
        provided examples.
    """
    k = mean_mu.size

    # if sigma is given as a diagonal, compute the matrix
    if corr_sigma.ndim == 1:
        corr_sigma = numpy.diag(corr_sigma)

    x_array = x_array - mean_mu
    factor = (2*numpy.pi)**(-k/2)*numpy.linalg.det(corr_sigma)**(-0.5)
    exp_part = numpy.exp(-0.5*numpy.sum(numpy.dot(x_array, numpy.linalg.pinv(corr_sigma))*x_array, axis=1))

    probabilities = factor*exp_part

    return probabilities


def pi_approx(n):
    """This function approximates pi.
    User inputs a number of points to choose
    from the space [-1, 1] x [-1, 1] and the
    function counts the number of times that
    the points fall inside the unit circle."""

    import numpy as np
    X = np.linspace(-1, 1, 100000)
    Y = X
    ctr = 0
    for _ in range(n):
        X_r = np.random.choice(X)
        Y_r = np.random.choice(Y)
        if X_r ** 2 + Y_r ** 2 <= 1:
            ctr += 1
    return 4 * ctr / n


import numpy as np

def softmax(a):
    e = np.exp(a)
    return e / e.sum(axis=1)[:, np.newaxis]

def sigmoid(a):
    return np.ones_like(a) / (np.ones_like(a) + np.exp(-a))

def d_sigmoid(a):
    return sigmoid(a) * (np.ones_like(a) - sigmoid(a))

def relu(a):
    return np.maximum(0, a)

class DifferentiableFunction(object):
    def __call__(self, x):
        raise NotImplementedError()
    def d(self, x):
        raise NotImplementedError()

class Sigmoid(DifferentiableFunction):
    def __call__(self, x):
        return sigmoid(x)
    def d(self, x):
        return d_sigmoid(x)

class Softmax(DifferentiableFunction):
    def __call__(self, x):
        return softmax(x)
    def d(self, x):
       return softmax(x) * (np.ones_like(x) - softmax(x))

class ReLu(DifferentiableFunction):
    def __call__(self, x):
        return relu(x)
    def d(self, x):
        return (x > 0).astype(np.float32)


import numpy as np
import matplotlib as mpl
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from scipy.integrate import RK45

# 3 time animated subplots will be saved when the code is run, they show:
# subplot 1: phase space; the contours corresponds to possible trajectories
# the trajectory depends on the initial conditions
# subplot 2: pendulum evolution in xy space
# subplot 3: energy evolution as a function of time

# solve the equations with forward Euler and dt=0.01 and dt=0.1
# what do you notice? is this expected behavior?

# implement a leapfrog algorithm. use dt=0.1. What changes?

# is leapfrog "better" or "worse" than Forward Euler? Why?

# implement a Verlet algorithm. How do energy evolution compare with leapfrog?

# what is the difference between leapfrog and Verlet in this case?

# Does Verlet algorithm conserve energy better than Leapfrog algorithm?

# Can you compare them in terms of computational cost?

# What do you have to keep into account for leapfrog/Verlet algorithm
# when calculating the energy?

# Now solve the equation numerically use the trapezoidal integration method:
# what is the energy evolution?

# The derived Runge-Kutta method is already implemented.
# Does RK4 conserve energy over time?

'Hamiltonian of the pendulum system'
def hamiltonian(phi, p):
    return 0.5*np.multiply(p,p) + 1. - np.cos(phi)

'X & Y coordinate of the pendulum'
def beam(phi):
    x = np.sin(phi)
    y = -np.cos(phi)
    return x, y

'functions to be used in Newton-Raphson method'
def f(dt, phi, p, new_value):
    return 2./dt*new_value + dt/2.*np.sin(new_value)-2./dt

def f_prime(dt, new_value):
    return

'Newton-Raphson method for solving pendulum position'
def Pendulum_eqn(dt, phi, p):
    delta = 1e-16
    new_value = 0.75
    iterations = 0
    for i in range(100):
        residual = 0.0 # What is the right expression for residual?

        if (abs(residual) < delta):
            iterations = i
            break

        "Newton - Raphson method"

    if (iterations == 0):
        print("Solution did not converge! Residue at: ", residual)
    return new_value, residual, iterations

"Timesteps"
dt = 0.01 # 0.01, 0.1
t_end = 20
n_step = int(t_end/dt)

"Initial Conditions"
phi_0=0
p_0=1.2

"Initial time and energy"
t = np.zeros(1)

"We can switch between methods here."
ODE_method = 'Verlet' # 'Forward', 'Leapfrog', 'Verlet', 'Integration', 'RK4'

if (ODE_method == 'Verlet'):
    "Initial condition for Verlet algorithm"
    # needs velocity info at time -1/2
    p = p_0 - dt/2. * np.sin(phi_0)
    phi= phi_0

elif (ODE_method == 'Leapfrog'):
    "Initial condition for Leapfrog algorithm"
    # needs spatial coordinate info at time 1/2
    phi = phi_0 + dt/2. * p_0
    p= p_0

else:
    phi=phi_0
    p=p_0

phi_v = [phi]
p_v = [p]

hist_x = []
hist_y = []
hist_phi = []
hist_p = []
for iters in range(n_step):
    if (ODE_method == 'Forward'):
        "Forward Euler"
        phi_new = phi + dt*p
        p_new = p - dt*np.sin(phi)

    elif (ODE_method == 'Leapfrog'):
        '''
        Leapfrog algorithm
        Since the phi value is already offset by half a step in the initial condition,
        the update to p_new and phi_new is ALMOST similar to Forward Euler.
        '''
        p_new = p - dt * np.sin(phi)
        phi_new = phi + dt* p_new

    elif (ODE_method == 'Verlet'):
        '''
        Verlet algorithm
        Since the p value is already offset by half a step in the initial condition,
        the update to p_new and phi_new is ALMOST similar to Forward Euler.
        '''
        p_new = p - dt * np.sin(phi)
        phi_new = phi + dt* p_new

    elif (ODE_method == 'Integration'):
        '''
        Integration method
        First, compute either phi_new or p_new using Newton-Raphson method.
        Highly recommend to solve for phi_new, since it is a much simpler expression.
        '''
        new_value, res, its = Pendulum_eqn(dt, phi, p)
        p_new = 0.0
        phi_new = 0.0

    elif (ODE_method == 'RK4'):
        '''
        Runge-Kutta 4th order method
        You must realise that phi is a function of p & t. phi = phi(t, p)
        Also understand that p is a function of phi & t. p = p(t, phi)
        Follow your derived steps to calculate phi_new!
        '''
        p1 = p
        phi1 = phi
        k1 = p1*dt

        p2 = p1 - np.sin(phi1 + k1*0.5)*dt*0.5
        k2 = p2*dt

        p3 = p1 - np.sin(phi1 + k2*0.5)*dt*0.5
        k3 = p3*dt

        p4 = p1 - np.sin(phi1 + k3)*dt
        k4 = p4*dt

        phi_new = phi + (k1 + 2.*k2 + 2.*k3 + k4)/6.

        'Next, calculate p_new'
        f1 = -np.sin(phi1)*dt
        phi2 = phi1 + (p1 + f1*0.5)*dt*0.5

        f2 = -np.sin(phi2)*dt
        phi3 = phi1 + (p1 + f2*0.5)*dt*0.5

        f3 = -np.sin(phi3)*dt
        phi4 = phi1 + dt*(p1 + f3)

        f4 = -np.sin(phi4)*dt

        p_new = p + (f1 + 2.*f2 + 2.*f3 + f4)/6.

    else:
        print('Invalid method chosen.')

    "Update the phi & p values"
    p_v.append(p_new)
    phi_v.append(phi_new)

    phi = phi_new
    p = p_new

    "Record and extend t "
    t = np.append(t, (iters+1)*dt)

    "Limit phi to range of [-4, 4]"
    if (phi > 4): phi = phi - 2.0*np.pi
    if (phi < -4): phi = phi + 2.0*np.pi

    x, y = beam(phi)

    hist_x.append(x)
    hist_y.append(y)
    hist_phi.append(phi)
    hist_p.append(p)

if ODE_method== 'Leapfrog':
    ## the spatial coordinate calculated in leapfrog is advanced of
    # hald a timestep w.r.t. momentum
    ## this is the ``real" time at which our phi are evaluated
    t_phi= t+ 1/2.*dt
    ## now interpolate to t
    phi_v= np.interp(t, t_phi, phi_v)
    # setting phi_0, since this cannot be calculated form interpolation
    phi_v[0]= phi_0

elif ODE_method== 'Verlet':
    ## the momentum calculated in Verlet is retarded of
    # hald a timestep w.r.t. spatial coordinate

    # calculating the last value of the moentum
    p_last= p_v[-1]- np.sin(phi_v[-1])*dt/2

    ## this is the ``real" time at which our phi are evaluated
    t_p= t- 1/2.*dt
    ## now interpolate to t
    p_v= np.interp(t, t_p, p_v)
    # fixing the last point, that cannot be calculated by interpolation
    p_v[-1]= p_last

"calculate energy"
E= hamiltonian(phi_v, p_v)

if ODE_method=='Leapfrog' or ODE_method=='Verlet' or ODE_method=='Integration':
    print('Method: ', ODE_method, 'DE=' , max(E)- min(E))

"Plotting related codes: "
"Phi & p phase space contour"
phi_contour, p_contour = np.meshgrid(np.linspace(-1.5*np.pi, 1.5*np.pi, 100), \
                                      np.linspace(-2.5, 2.5, 50))

h = hamiltonian(phi_contour, p_contour)

fig = plt.figure(constrained_layout=False)
fig.set_size_inches(9, 6)#(18.5, 9.0)
gs = fig.add_gridspec(ncols=2, nrows=2)

"preparing subplot for phase space contour plot"
ax = fig.add_subplot(gs[0, 0])
ax.contourf(phi_contour, p_contour, h)
ax.set_aspect('equal')
ax.set_xlabel('phi')
ax.set_ylabel('p')

"preparing subplot for pendulum position plot"
ax1 = fig.add_subplot(gs[0, 1])
ax1.set_ylim(-1.5, 1.5)
ax1.set_xlim(-1.5, 1.5)
ax1.set_aspect('equal')
ax1.set_xlabel('x')
ax1.set_ylabel('y')

"preparing subplot for energy over time plot"
ax2 = fig.add_subplot(gs[1, 0])
ax2.set_xlim(min(t), max(t))
ax2.set_ylim(min(E), max(E))
ax2.grid()
ax2.set_xlabel('t')
ax2.set_ylabel('E')

point, = ax.plot([phi_0], [p_0], 'or')
line, = ax1.plot([], [], 'o-', lw=2)
E_graph, = ax2.plot([], [])

"function to set compute data into animation frames"
def animate(i):
    t_list = t[:i]
    E_list = E[:i]
    thisx = [0, hist_x[i]]
    thisy = [0, hist_y[i]]

    point.set_data(hist_phi[i],hist_p[i])
    line.set_data(thisx, thisy)
    E_graph.set_data(t_list, E_list)
    return point, line, E_graph,

ani = animation.FuncAnimation(
    fig, animate, len(hist_y), interval=dt*1000, blit=True)

# ani.save('Pendulum_swing.mp4', fps=30, extra_args=['-vcodec', 'libx264'])
plt.show()


"""
A3, Q2
Solving van der Pol oscillator using both Runge-Kutta and odeint,
taking odeint's result to be the true value
"""
import scipy.integrate as sp_int
import numpy as np
import matplotlib.pyplot as plt
from run_kut4 import *

# define constant
epsilon = 10.0
# inital conditions
x0 = 0.5
y0 = 0.0
ics = [x0,y0] #vector of initial conditions

# define the function as it demands
def f(x,y):
    f = np.zeros(2)
    f[0] = y[1] #f0 == dx, y[0] = x, y[1] = y
    f[1] = (-y[0])-((epsilon*((y[0]**2)-1))*y[1]) #f1 == dy
    return f

# integration parameters. These won't change
x_start = 0.0
x_stop = (10.0*np.pi)
# integration itself
X_002,Y_002 = integrate(f,x_start,ics,x_stop,0.02)

# plot
plt.figure(1)
plt.plot(X_002,Y_002[:,0],label=r'$h$ = 0.02',color='b')
plt.xlabel(r'time, $t$')
plt.ylabel(r'position, $x$')
plt.title(r'Position vs. time for step-size $h$ = 0.02')
plt.legend(loc=0)
plt.xlim(0,(10*np.pi))
plt.show()

# again for h=0.05
X_005,Y_005 = integrate(f,x_start,ics,x_stop,0.05)

plt.figure(2)
plt.plot(X_005,Y_005[:,0],label=r'$h$ = 0.05',color='g')
plt.xlabel(r'time, $t$')
plt.ylabel(r'position, $x$')
plt.title(r'Position vs. time for step-size $h$ = 0.05')
plt.xlim(0,(10*np.pi))
plt.legend(loc=0)
plt.show()

# again for h=0.1
X_01,Y_01 = integrate(f,x_start,ics,x_stop,0.1)

plt.figure(3)
plt.plot(X_01,Y_01[:,0],label=r'$h$ = 0.1', color='r')
plt.xlabel(r'time, $t$')
plt.ylabel(r'position, $x$')
plt.title(r'Position vs. time for step-size $h$ = 0.1')
plt.legend(loc=0)
#plt.ylim(-2.1,2.1)
plt.xlim(0,(10*np.pi))
plt.show()

# define equations for odeint
def odeint_f(q,t):
    xi = q[0]
    yi = q[1]
    f0 = yi
    f1 = (-xi)-((epsilon*((xi**2)-1))*yi)
    return [f0,f1]

# range to solve for
t = np.linspace(0,(10*np.pi), 100000) #make this huge to get pretty curves
# integrate using odeint
odeint_sol_10 = sp_int.odeint(odeint_f,ics,t)
x_ois_10 = odeint_sol_10[:,0]
y_ois_10 = odeint_sol_10[:,1]

# plot this solution
plt.figure(4)
plt.plot(t,x_ois_10, label='odeint', color='k')
plt.xlabel(r'time, $t$')
plt.ylabel(r'position, $x$')
plt.title(r'Position vs. time, using odeint')
plt.xlim(0,(10*np.pi))
plt.show()

# plot all four
plt.figure(5)
plt.plot(X_01,Y_01[:,0], label=r'RK: $h$ = 0.1', color='r') ##this blows up
plt.plot(X_005,Y_005[:,0], label=r'RK: $h$ = 0.05', color='g')
plt.plot(X_002,Y_002[:,0], label=r'RK: $h$ = 0.02', color='b')
plt.plot(t,x_ois_10, label='odeint', color='k')
plt.xlabel(r'time, $t$')
plt.ylabel(r'position, $x$')
plt.title('Position vs. time')
plt.legend(loc=0)
plt.xlim(0,(10*np.pi))
plt.ylim(-2.1,2.1)
plt.show()

# Errors.
# Both arrays need to be of equal size.
# Generate new true value arrays to match the size of the rk arrays

def trange(x):
    z = np.linspace(0,10*np.pi,len(x[:,0]))
    return z

truev_002 = sp_int.odeint(odeint_f,ics,trange(Y_002))
truev_005 = sp_int.odeint(odeint_f,ics,trange(Y_005))
truev_01 = sp_int.odeint(odeint_f,ics,trange(Y_01))

er_rk_002 = np.abs((truev_002[:,0]) - (Y_002[:,0]))
er_rk_005 = np.abs((truev_005[:,0]) - (Y_005[:,0]))
er_rk_01 = np.abs((truev_01[:,0]) - (Y_01[:,0]))

plt.figure(6)
plt.semilogy(trange(Y_002),er_rk_002, label=r'Error for RK sol with $h$ = 0.02')
plt.semilogy(trange(Y_005),er_rk_005, label=r'Error for RK sol with $h$ = 0.05')
plt.semilogy(trange(Y_01),er_rk_01, label=r'Error for RK sol with $h$ = 0.1')
plt.xlim(0,10*np.pi)
plt.title(r'Graph of log of absolute errors vs. $t$')
plt.xlabel(r'$t$, time')
plt.ylabel(r'semilog($\sigma$), absolute error')
plt.legend(loc=0)
plt.show()

plt.figure(7)
plt.semilogy(trange(Y_002),er_rk_002, label=r'Error for RK sol with $h$ = 0.02')
plt.semilogy(trange(Y_005),er_rk_005, label=r'Error for RK sol with $h$ = 0.05')
plt.xlim(0,10*np.pi)
plt.title(r'Graph of log of absolute errors vs. $t$')
plt.xlabel(r'$t$, time')
plt.ylabel(r'semilog($\sigma$), absolute error')
plt.legend(loc=0)
plt.show()

# C
# get epsilon = 4 sol
epsilon = 4.0
odeint_sol_4 = sp_int.odeint(odeint_f,ics,t)
x_ois_4 = odeint_sol_4[:,0]
y_ois_4 = odeint_sol_4[:,1]

# do again for epsilon = 1
epsilon = 1.0
odeint_sol_1 = sp_int.odeint(odeint_f,ics,t)
x_ois_1 = odeint_sol_1[:,0]
y_ois_1 = odeint_sol_1[:,1]

plt.figure(8)
plt.plot(x_ois_1,y_ois_1, label=r'$\epsilon$ = 1', color='b')
plt.plot(x_ois_4,y_ois_4, label=r'$\epsilon$ = 4', color='g')
plt.plot(x_ois_10,y_ois_10, label=r'$\epsilon$ = 10', color='r')
plt.axis('equal')
plt.title(r'Phase-space plot, $\frac{dx}{dt}$ vs. $x$')
plt.xlabel(r'$x$, position')
plt.ylabel(r'$\frac{dx}{dt}$, velocity')
plt.legend(loc=0)
plt.show()


# -*- coding: utf-8 -*-
"""
Created on Mon Oct 21 10:01:29 2019

@author: BSWOOD9321
"""

import numpy as np
import matplotlib.pyplot as plt

k=int(input('Enter a spring constant: '))
m=int(input('Enter a mass: '))
dt=.01
t=0
x=int(input('Enter a starting position: '))
v=int(input('Enter a starting velocity: '))
p=int(input('Enter an EVEN p-value between 2-12: '))
T=[t]
X=[x]
V=[v]

while t<15:
    f=-k*x**(p-1)
    v=v+(f/m)*dt
    x=x+v*dt
    t=t+dt
    T.append(t)
    X.append(x)
    V.append(v)

plt.plot(T,X,label='position')
plt.plot(T,V,label='velo')
plt.grid()
plt.legend(loc=(1.04,0))
plt.xlabel('Time')

plt.show()

## 1. Yes, I get the result that I expect, a sinusoidal pattern of the spring "bouncing" back and forth.
## 2. Yes, no matter what p value we choose, the solution does remain periodic with a constant amplitude. Some of them look really cool.
## 3. The mass has its highest velocity as the spring crosses "zero" in either direction. This does make sense as before this point,
## the mass is still acceleration faster, and after this point, it is de-accelerating until it reaches its maximum x value.
## 4. Yes, oscillators with different amplitudes have different periods. We can probably "design" two different amplitudes with the same periods,
## but it would require different masses and/or spring constants.

k1=3
m1=2
x1=0
v1=5
p1=2

while p1<=12:
    t1=0
    T1=[t1]
    X1=[x1]
    V1=[v1]
    while t1<15:
        f=-k1*x1**(p1-1)
        v1=v1+(f/m1)*dt
        x1=x1+v1*dt
        t1=t1+dt
        T1.append(t1)
        X1.append(x1)
        V1.append(v1)
    plt.plot(T1,X1,label='position')
    plt.plot(T1,V1,label='velo')
    plt.legend(loc=(1.04,0))
    plt.show()
    print('P=',p1,', k=3, m=2')
    p1=p1+2


#py_precision.py

"""A brief foray into the precision available with different numeric
   data types and some Python libraries you can use to wrangle them."""

#floats controlled by hardware; accurate to nearest binary value
print("simple addition: {} + {} + {} = {}".\
      format(.1, .1, .1,
             .1 + .1 + .1))
print()

#another example - these two are the same in binary
a = .1
b =  0.10000000000000001
print("does {} = {:.23f}?   {}".\
      format(a, b, a == b))
print()

#one solution:  store as a fraction
print("as fraction: {} + {} + {} = {}".\
      format(a, a, a,  (a + a + a).as_integer_ratio()))

print()
#better yet, use fractions library
import fractions
a = fractions.Fraction(1, 10)
print("using fractions.Fraction: {} + {} + {} = {}".\
      format(a, a, a,  (a + a + a)))

print()
#or use the decimal library
import decimal

#Accuracy is baked in when the (immutable) number is defined.

#   The accuracy is implied by the number of decimal places provided.
a = decimal.Decimal(".111111111111111111111111111111111111111111111111111111111111111111111"
                    "11111111111111111111111111111111111111111111111111111111111111111111"
                    "11111111111111111111111111111111111111111111111111111111111111111111"
                    "11111111111111111111111111111111111111111111111111111111111111111111"
                    "11111111111111111111111111111111111111111111111111111111111111111111"
                    "11111111111111111111111111111111111111111111111111111111111111111111")

#the 'context' is used to specify how much precision will be used in operations
decimal.getcontext().prec = 600
print("using decimal.Decimal:\n{}\n+\n{}\n+\n{}\n=\n{}".\
      format(a, a, a,
             a + a + a))

#and you can always 'sqeeze it down' into a fixed precision
print()
quant_num =  a.quantize(decimal.Decimal('0.001'))
print("quantized:  a = {}".format(quant_num))

print()

#Integers are inherently accurate.  There is no theoretical size limit
# except practical (system constrained).  How big?
import sys

print("maximum Python integer supported on this box: {}".format(sys.maxsize))

print()
#In numpy (scipy) different object types have different limitations
import numpy
print("numpy integers available on this box:")
for dtype in numpy.sctypes['int']:
	info =  numpy.iinfo(dtype)
	print("dtype: {}  max: {}".format(info.dtype, info.max))


from sympy import *

q0, q1, q2, q3 = symbols('q0 q1 q2 q3')
quaternion_symbols = [q0, q1, q2, q3]
target_value = 1
model_function = sqrt(q0*q0 + q1*q1 + q2*q2 + q3*q3)
delta = Matrix([target_value - model_function]).vec()
delta_jacobian = delta.jacobian(quaternion_symbols)

print(delta)
print(delta_jacobian)

with open("quaternion_constraint_jacobian.h",'w') as f_cpp:
    f_cpp.write("inline void quaternion_constraint(double &delta, double q0, double q1, double q2, double q3)\n")
    f_cpp.write("{")
    f_cpp.write("delta = %s;\n"%(ccode(delta[0,0])))
    f_cpp.write("}")
    f_cpp.write("\n")
    f_cpp.write("inline void quaternion_constraint_jacobian(Eigen::Matrix<double, 1, 4> &j, double q0, double q1, double q2, double q3)\n")
    f_cpp.write("{")
    for i in range (1):
        for j in range (4):
            f_cpp.write("j.coeffRef(%d,%d) = %s;\n"%(i,j, ccode(delta_jacobian[i,j])))
    f_cpp.write("}")


"""
Metrics for distances and losses
"""

import numpy as np

def euclidean_distance(a, b):
    """
    Euclidean distance: https://en.wikipedia.org/wiki/Euclidean_distance
    """

    squared_diff = (a - b) ** 2
    sum_squared_diff = np.sum(squared_diff)
    return np.sqrt(sum_squared_diff)

def mean_squared_error(y_true, y_pred):
    """Compute the Mean Squared Error between two vectors."""

    return np.square(y_true - y_pred).mean()


import numpy as np

def softmax(output):
  return np.transpose(np.exp(np.transpose(output)) / np.sum(np.exp(output), axis=1))

def ce_loss(output, label):
  return np.mean(-np.sum(label * np.log(output), axis=1))

def grad(input, output, label):
  return (-1/input.shape[0])*(np.transpose(input)@(label - output))

def softmax_classifier(w, input, label, lamda):
    """
      Softmax Classifier

      Inputs have dimension D, there are C classes, a minibatch have N examples.
      (In this homework, D = 784, C = 10)

      Inputs:
      - w: A numpy array of shape (D, C) containing weights.
      - input: A numpy array of shape (N, D) containing a minibatch of data.
      - label: A numpy array of shape (N, C) containing labels, label[i] is a
        one-hot vector, label[i][j]=1 means i-th example belong to j-th class.
      - lamda: regularization strength, which is a hyerparameter.

      Returns:
      - loss: a single float number represents the average loss over the minibatch.
      - gradient: shape (D, C), represents the gradient with respect to weights W.
      - prediction: shape (N, 1), prediction[i]=c means i-th example belong to c-th class.
    """

    ############################################################################
    # TODO: Put your code here
    # Output -> Softmax of W*X
    output = softmax(input @ w)
    # Loss -> Cross-entropy + Regularization
    loss = ce_loss(output, label) + (lamda/2)*np.linalg.norm(w)
    # Softmax gradient
    gradient = grad(input, output, label)
    # Predicted label
    prediction = np.argmax(output, axis=1)
    ############################################################################

    return loss, gradient, prediction


import numpy as np

def cart2sph(x,y,z):
    '''
    直角坐标转化为球坐标，但此处的[x,y,z]对应标准球坐标系中的[y,z,x]

    输入:
        * x,y,z 直角坐标

    输出:
        * azimuth, elevation, r 角度值的球坐标
    '''
    r2d = 180.0/np.pi
    azimuth = np.arctan2(x,z)*r2d
    elevation = np.arctan2(y,np.sqrt(z**2 + x**2))*r2d
    r = np.sqrt(z**2 + x**2 + y**2)
    return [azimuth, elevation, r]

def sph2cart(azimuth,elevation,r):
    '''
    球坐标转化为直角坐标，但此处的[x,y,z]对应标准球坐标系中的[y,z,x]

    输入:
        * azimuth, elevation, r 角度值的球坐标

    输出:
        * x,y,z 直角坐标
    '''
    d2r = np.pi/180.0
    z = r * np.cos(elevation*d2r) * np.cos(azimuth*d2r)
    x = r * np.cos(elevation*d2r) * np.sin(azimuth*d2r)
    y = r * np.sin(elevation*d2r)
    return [x, y, z]


from __future__ import division
import numpy as np
import math as mt
import decimal as dm
import time as tm
'''-----------------------------------------------------------------------------
                        Auxiliar Functions:
-----------------------------------------------------------------------------'''
error = lambda y,x: y*(10**(-x))

F = lambda x: (10*mt.exp(-0.5*x)*mt.cos(2*x) -4) #F(x)

F_deriv = lambda x: mt.exp(-0.5*x)*(-20*mt.sin(2*x)-5*mt.cos(2*x)) #F'(x)

F_dderiv = lambda x: mt.exp(-0.5*x)*(20*mt.sin(2*x)-37.5*mt.cos(2*x)) #F''(x)

f = lambda x: 0.5*(mt.acos((2*mt.exp(0.5*x))/5))#f(x)=1/2*arcos(2e^0.5x/5)

'''-----------------------------------------------------------------------------
                        Iterative Method:
-----------------------------------------------------------------------------'''

def itMethod(x0,eps):
    x1=f(x0)
    err=abs(x1-x0)
    i=1
    while err > eps:
        x0=x1
        x1=f(x0)
        err=abs(x1-x0)
        i+=1
    print("Iterações: "+str(i)+"\n")
    print("Raíz: "+str(dm.Decimal(x1))+"\n")
    print("Erro: "+str(dm.Decimal(err))+"\n")
    return

'''-----------------------------------------------------------------------------
                        Newton Method:
-----------------------------------------------------------------------------'''
def newtonMethod(x0,a,b,x_der_min,x_dder_max,eps):
    min_fd=F_deriv(x_der_min) #F' min value
    max_fdd=F_dderiv(x_dder_max) #F'' max value
    m=(1/2)*(min_fd/max_fdd) #multiplier
    err=abs(b-a) #x0 error
    x1=0
    i=0; #iterations
    while(err>eps):
        x1=x0-(F(x0)/F_deriv(x0))
        err=m*(err**2)
        x0=x1
        i+=1
    print("Iterações: "+str(i)+"\n")
    print("Raíz: "+str(dm.Decimal(x0))+"\n")
    print("Erro: "+str(dm.Decimal(err))+"\n")
    return

#Tests-------------------------------------------
print("Newton Method:\n")
newtonMethod(0.54,0.5,0.54,0.5,0.5,error(5,12))

print("Itera Method:\n")
itMethod(0.54,error(5,12))


import numpy as np
from numpy.linalg import norm
from functools import wraps
from time import time

def power_vec(vector):
    return np.square(np.abs(vector))

def power_tot(vector):
    powers = power_vec(vector)
    return np.sum(powers)

def normalize_vec(vector):
    P = power_tot(vector)
    return vector / np.sqrt(P)

def normalize_pow(vector):
    return vector / np.sum(vector)

def MSE(vector1, vector2):
    vector1 = np.reshape(vector1, (-1,))
    vector2 = np.reshape(vector2, (-1,))
    return np.sum(np.square(vector1 - vector2))/vector1.size


import numpy as np
import matplotlib.pyplot as plt
from PIL import Image

max_iter = 80
divergence_radius = 4
canvas_shape = (600, 600)

def mandel_iter(canvas):

    real_c, cmplx_c = canvas
    real_z, cmplx_z = np.zeros(canvas_shape, dtype="float64"), np.zeros(
        canvas_shape, dtype="float64"
    )

    iter_array = np.zeros(canvas_shape, dtype="uint8")
    done_mask = np.zeros(canvas_shape, dtype="uint8")

    for idx in range(max_iter):

        # (a + bi) ** 2

        # a**2 - b**2
        # 2 * a * b

        real_z, cmplx_z = (
            real_z ** 2 - cmplx_z ** 2 + real_c,
            2 * real_z * cmplx_z + cmplx_c,
        )

        dists = real_z ** 2 + cmplx_z ** 2

        done_mask[dists >= divergence_radius] = 1
        # done_mask = done_mask | (dists >= divergence_radius)

        real_z[done_mask == 1] = 0
        cmplx_z[done_mask == 1] = 0

        iter_array[done_mask != 1] = idx

        if np.sum(done_mask == 0) == 0:
            break

    return iter_array

def mandelbrot(zoom=0.5, center=(0, 0), filename="deneme.png"):
    _extents = 2 / (2 ** zoom)

    x_lim = [center[0] - _extents, center[0] + _extents]
    y_lim = [center[1] - _extents, center[1] + _extents]

    x_range = np.linspace(x_lim[0], x_lim[1], canvas_shape[1], dtype="float64").reshape(
        1, canvas_shape[1]
    )
    canvas_real = np.repeat(x_range, canvas_shape[0], axis=0)
    y_range = np.linspace(y_lim[0], y_lim[1], canvas_shape[0], dtype="float64").reshape(
        canvas_shape[0], 1
    )
    canvas_cmplx = np.repeat(y_range[::-1, :], canvas_shape[1], axis=1)

    res = mandel_iter((canvas_real, canvas_cmplx))

    plt.imshow(res)
    plt.set_cmap("hot")
    plt.axis("off")
    plt.savefig(filename, bbox_inches="tight")

"""
BYTIE REPO : https://github.com/jbytecode/bytie

1 - mandelbrot çizdir : mrgranddy

2 - parametre alıp zoomdur/scale
    yaparak çizdir

2.5 - opsiyonel:
      eğer ilginç bölgeleri bulmanın
      bir yolu varsa onu bul

3 - bunun bi apisini yap

4 - bytie'ye bağla

"""


import p2_data as data
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import OneHotEncoder
import time

# *****************************************************************
# ====================helper functions=========================

def NLL(X, y, W, reg=0.0):
	'''
		Calculate negative log likelihood for softmax regression.
	'''
	# YOUR CODE GOES BELOW
	mu = X @ W # m x k
	exp_mu = np.exp(mu)
	prob = exp_mu / exp_mu.sum(axis=1).reshape(-1, 1)
	groundTruth = y * np.log(prob)
	NLL = -groundTruth.sum(axis=1).sum() + reg * np.diag(W.T @ W).sum()

	return NLL

def grad_softmax(X, y, W, reg=0.0):
	'''
		Return the gradient of W for softmax regression.
	'''
	# YOUR CODE BELOW
	mu = X @ W
	exp_mu = np.exp(mu)
	prob = exp_mu / exp_mu.sum(axis=1).reshape(-1, 1)
	grad = X.T @ (prob - y) + reg * W

	return grad

def predict(X, W):
	'''
		Return y_pred with dimension m x 1.
	'''
	# YOUR CODE BELOW
	mu = X @ W
	exp_mu = np.exp(mu)
	prob = exp_mu / exp_mu.sum(axis=1).reshape(-1, 1)
	y_pred = np.argmax(prob, axis=1).reshape(-1, 1)
	return y_pred

def get_accuracy(y_pred, y):
	'''
		Return the percentage of same bits between y_pred and y.
	'''
	diff = (y_pred == y).astype(int)
	accu = 1. * diff.sum() / len(y)
	return accu

def grad_descent(X, y, reg=0.0, lr=1e-5, eps=1e-6, \
	max_iter=500, print_freq=20):
	'''
		X is matrix with dimension m x (n + 1).
		y is label with dimension m x 1.
		reg is the parameter for regularization.
		lr is the learning rate.
		eps is the threshold of the norm for the gradients.
		max_iter is the maximum number of iterations.
		print_freq is the frequency of printing the report.

		Return the optimal weight by gradient descent and
		the corresponding learning objectives.
	'''
	m, n = X.shape
	k = y.shape[1]
	nll_list = []
	# initialize the weight and its gradient
	W = np.zeros((n, k))
	W_grad = np.ones((n, k))
	print('==> Running gradient descent...')
	iter_num = 0
	t_start = time.time()
	# Running the gradient descent algorithm
	# Update W
	# Calculate learning objectives
	# YOUR CODE GOES BELOW
	while iter_num < max_iter and np.linalg.norm(W_grad) > eps:
		# calculate NLL
		nll = NLL(X, y, W, reg=reg)
		if np.isnan(nll):
			break
		nll_list.append(nll)
		# calculate gradients and update W
		W_grad = grad_softmax(X, y, W, reg=reg)
		W -= lr * W_grad

		if (iter_num + 1) % print_freq == 0:
			print('-- Iteration {} - \
				negative log likelihood {: 4.4f}'.format(iter_num + 1, nll))
		iter_num += 1
	# benchmark
	t_end = time.time()
	print('-- Time elapsed for running gradient descent: {t:2.2f} \
		seconds'.format(t=t_end - t_start))

	return W, nll_list

def accuracy_vs_lambda(X_train, y_train_OH, X_test, y_test, lambda_list):
	'''
		Generate accuracy for all given regularization parameters.
		Generate a plot of accuracy vs lambda.
		Return the lambda with optimal accuracy.
	'''
	# Find corresponding accuracy values for each parameter
	accu_list = []
	# YOUR CODE BELOW
	for reg in lambda_list:
		W, nll_list = grad_descent(X_train, y_train_OH, reg=reg, lr=2e-5, \
		print_freq=50)
		y_pred = predict(X_test, W)
		accuracy = get_accuracy(y_pred, y_test)
		accu_list.append(accuracy)
		print('-- Accuracy is {:2.4f} for lambda = {:2.2f}'.format(accuracy, reg))
	# Plot accuracy vs lambda
	print('==> Printing accuracy vs lambda...')
	plt.style.use('ggplot')
	plt.plot(lambda_list, accu_list)
	plt.title('Accuracy versus Lambda in Softmax Regression')
	plt.xlabel('Lambda')
	plt.ylabel('Accuracy')
	plt.savefig('ACCURACY_vs_LAMBDA_2b.png', format = 'png')
	plt.close()
	print('==> Plotting completed.')
	# Find optimal lambda
	opt_lambda_index = np.argmax(accu_list)
	reg_opt = lambda_list[opt_lambda_index]
	return reg_opt

# *****************************************************************
# ====================main driver function: Softmax=========================
if __name__ == '__main__':
	# STEP 0: LOADING DATA
	df_train = data.df_train
	df_test = data.df_test
	'''
		X is a matrix with dimension m x n
		y is a vector with dimension m x 1
	'''
	X_train = data.X_train
	y_train = data.y_train
	X_test = data.X_test
	y_test = data.y_test
	# stacking an array of ones
	X_train = np.hstack((np.ones_like(y_train), X_train))
	X_test = np.hstack((np.ones_like(y_test), X_test))
	# one hot encoder
	enc = OneHotEncoder()
	y_train_OH = enc.fit_transform(y_train.copy()).astype(int).toarray()
	y_test_OH = enc.fit_transform(y_test.copy()).astype(int).toarray()

	# STEP 1: Accuracy versus lambda
	print('==> Step 1: Finding optimal regularization parameter...')
	# Fill in the code in NLL, grad_softmax, and grad_descent
	# Then, fill in predict and accuracy_vs_lambda
	lambda_list = [0.01, 0.1, 0.5, 1.0, 10.0, 50.0, 100.0, 200.0, 500.0, 1000.0]
	reg_opt = accuracy_vs_lambda(X_train, y_train_OH, X_test, y_test, lambda_list)
	print('-- Optimal regularization parameter is {:2.2f}'.format(reg_opt))
	# STEP 2: Convergence plot
	W_gd, nll_list_gd = grad_descent(X_train, y_train_OH, reg=reg_opt, max_iter=1500, lr=2e-5, \
		print_freq=100)
	print('==> Step 2: Plotting convergence plot...')
	plt.style.use('ggplot')
	# Plot the learning curve of NLL vs Iteration
	# YOUR CODE BELOW
	nll_gd_plot, = plt.plot(range(len(nll_list_gd)), nll_list_gd)
	plt.setp(nll_gd_plot, color = 'red')
	plt.title('Convergence Plot on Softmax Regression with $\lambda = {:2.2f}$'.format(reg_opt))
	plt.xlabel('Iteration')
	plt.ylabel('NLL')
	plt.savefig('CONVERGENCE_2b.png', format = 'png')
	plt.close()
	print('==> Plotting completed.')


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

'''
In a submodule analysis implement a function that given the water level time history (dates, levels)
for a station computes a least-squares fit of a polynomial of degree p to water level data. The function
should return a tuple of (i) the polynomial object and (ii) any shift of the time (date) axis (see below).
The function should have the signature:

def polyfit(dates, levels, p):

Typical usage for a polynomial of degree 3 would be:
poly, d0 = polyfit(dates, levels, 3)
where poly is a numpy.poly1d object an d0 is any shift of the date (time) axis.
'''

# Task 2F
def polyfit(dates, levels, p):
    dates_coord = []
    for each_date in dates:
        dates_float = matplotlib.dates.date2num(each_date)
        dates_coord.append(dates_float)

    x = dates_coord
    y = levels
    d0 = x[-1]
    p_coeff = np.polyfit(x - d0, y, p)
    poly = np.poly1d(p_coeff)
    return poly, d0

    '''

    # Create set of 10 data points on interval (1000, 1002)
x = np.linspace(10000, 10002, 10)
y = [0.1, 0.09, 0.23, 0.34, 0.78, 0.74, 0.43, 0.31, 0.01, -0.05]

# Using shifted x values, find coefficient of best-fit
# polynomial f(x) of degree 4
p_coeff = np.polyfit(x - x[0], y, 4)

# Convert coefficient into a polynomial that can be evaluated
# e.g. poly(0.3)
poly = np.poly1d(p_coeff)

# Plot original data points
plt.plot(x, y, '.')

# Plot polynomial fit at 30 points along interval (note that polynomial
# is evaluated using the shift x)
x1 = np.linspace(x[0], x[-1], 30)
plt.plot(x1, poly(x1 - x[0]))

# Display plot
plt.show()
    '''


from Sequences.Simple import naturals, arithmetic
from Sequences.Manipulations import partial_prods, prepend, hypersequence, differences, offset
from Sequences.NiceErrorChecking import require_integers, require_geq, require_iterable

from collections import defaultdict
from sympy import sieve, isprime
from math import prod

##############################
## CLASSES OF PRIME NUMBERS ##
##############################

def primes():
    """
    Prime Numbers: Positive integers with exactly two factors\n
    OEIS A000040
    """

    upper = 2
    while True:
        upper *= 2
        yield from sieve.primerange(upper//2, upper)

def odd_primes():
    """
    Odd Primes: Primes that are odd positive integer\n
    OEIS A065091
    """

    yield from offset(primes(),1)

def twin_primes():
    """
    Twin Primes: Primes that are two more or less than another prime\n
    OEIS A001097
    """

    P = primes()

    a,b,c = next(P),next(P),next(P)

    while True:
        if b-2 == a or b+2 == c:
            yield b
        a,b,c = b,c,next(P)

def twin_prime_pairs():
    """
    Twin Prime Pairs: Primes that are two more or less than another prime\n
    OEIS A077800
    """

    P = primes()

    a,b = next(P),next(P)

    while True:
        if a+2 == b:
            yield (a,b)
        a,b = b,next(P)

def n_gap_prime_pairs(n):
    """
    Pairs of primes (p,q) such that p+n = q\n
    OEIS A077800, A156274, A156320, A140445, A156323, A140446, A272815,
         A156328, A272816, A140447
    """

    require_integers(["n"],[n])
    require_geq(["n"],[n],1)

    if n % 2 == 1:
        raise ValueError("n must be even, for odd n either there is no pair or the only pair is (2,2+n)")

    else:
        P = primes()
        lo = next(P)
        hi = [next(P)]

        while True:
            while hi[-1] < lo+n:
                hi.append(next(P))

            if lo+n in hi:
                yield (lo,lo+n)

            lo = hi.pop(0)

def prime_tuples(K):
    """
    Tuples of primes (p,q,r...) with gaps specified by K\n
    OEIS
    """

    for gap in K:
        if gap % 2 != 0:
            raise ValueError("gaps must be even, the only odd gaps are between 2 and another prime")

    P = primes()
    L = []

    def all_gaps(K,L):
        T = tuple([L[0]+k for k in K])
        for t in T:
            if t not in L:
                return ()
        return T

    while True:
        while len(L) <= len(K):
            L.append(next(P))

        while L[-1] <= L[0]+(max(K)):
            L.append(next(P))

        T = all_gaps(K,L)
        if T:
            yield T

        L = L[1:]

def superprimes():
    """
    Prime Indexed Primes: The pth primes for p in primes\n
    OEIS A006450
    """

    yield from hypersequence(primes())

def sophie_germain_primes():
    """
    Sophie Germain Primes: Primes such that 2p+1 is prime\n
    OEIS A005384
    """

    S = set([2])

    for p in odd_primes():
        S.add(p)
        if (p-1)//2 in S:
            yield (p-1)//2

        S = {s for s in S if s > (p-1)//2}

def safe_primes():
    """
    Safe Primes: Primes such that (p-1)/2 is prime\n
    OEIS A005385
    """

    S = set([2])

    for p in odd_primes():
        S.add(p)
        if (p-1)//2 in S:
            yield p

        S = {s for s in S if s > (p-1)//2}

def pythagorean_primes():
    """
    Pythagorean Primes: Primes that can be the hypotenuse of an integer right triangle\n
    OEIS A002144
    """

    for p in primes():
        if (p-1)%4 == 0:
            yield p

def linear_primes(a,b):
    """
    Primes of the form a*n+b for naturals n

    Args:
        a -- multiplicative constant
        b -- additive constant

    OEIS A002145
    """

    for p in primes():
        if (p-a)%b == 0:
            yield p

def congruent_primes(n,k):
    """
    Primes that are congruent to n modulo k

    Args:
        n -- remainder
        k -- modulus

    OEIS A002145
    """

    for p in primes():
        if p%k == n:
            yield p

def blum_primes():
    """
    Primes such that P = 2p+1 and p = 2q+1 where p and q are odd primes\n
    OEIS
    """

    for p in congruent_primes(3,4):
        a = (p-1)//2
        b = (a-1)//2

        if a%2 == 1 and b % 2 == 1:
            if isprime(a) and isprime(b):
                yield p

#####################
## CLOSELY RELATED ##
#####################

def prime_gaps():
    """
    Prime Gaps: Gaps between successive primes\n
    OEIS A001223
    """

    yield from differences(primes())

def primorial():
    """
    Primoral Numbers: Cumulative product of primes\n
    OEIS A002110
    """

    yield from  prepend(1,partial_prods(primes()))

def prime_powers():
    """
    Prime Powers: Powers of prime numbers\n
    OEIS A000961
    """

    yield 1

    D = defaultdict(list)

    for q in naturals(2):
        if q not in D:
            yield q
            D[q + q] = [q]

        else:
            if len(D[q]) == 1:
                yield q

            for p in D[q]:
                D[p+q].append(p)

            del D[q]

def prime_characteristic():
    """
    Characteristic Function of the Primes: For each positive integer 1 if the number is prime otherwise 0\n
    OEIS A010051
    """

    cur = 1

    for p in primes():
        for i in range(p-cur):
            yield 0

        yield 1

        cur = p+1

def prime_counting():
    """
    Prime Counting Function: Count of primes less than each non-negative integer\n
    OEIS A000720
    """

    ctr = 0
    cur = 0

    for p in primes():
        for i in range(p-cur):
            yield ctr

        ctr += 1
        cur = p

def prime_product(P,inclusive=False):
    """
    Naturals such that all prime factors are in the set P

    Args:
        P -- an interable consisting of primes
        inclusive -- bool, if True only numbers divisible by all elements of P are included

    OEIS
    """

    require_iterable(["P"],[P])

    P = set(P)
    I = prod(P)

    for p in P:
        if not isprime(p):
            raise Exception(f"All elements of P must be prime, {p} is not prime")

    if inclusive:
        for n in arithmetic(I,I):
            m = n

            for p in P:
                while m % p == 0:
                    m //= p

            if m == 1:
                yield n

    else:
        for n in naturals(2):
            m = n

            for p in P:
                while m % p == 0:
                    m //= p

            if m == 1:
                yield n

def prime_signatures():
    """
    Prime Signatures: The prime signature of each positive integer, returns tuples\n
    OEIS A124010
    """

    D = defaultdict(list)

    yield ()

    for q in naturals(2):
        if q not in D:
            yield (1,)
            D[q + q] = [q]

        else:
            T = []
            r = q

            for p in D[q]:
                T.append(0)
                D[p+q].append(p)

                while r % p == 0:
                    r //= p
                    T[-1] += 1

            yield tuple(T)

            del D[q]

def odius_signature():
    """
    Positive integers which have an odd number of 1s in the binary expansions of their prime signatures
    OEIS A000028
    """

    for n,f in enumerate(prime_signatures(),1):
        total = 0
        for m in f:
            while m != 0:
                m,r = divmod(m,2)
                total ^= r
        if total == 1:
            yield n

def evil_signature():
    """
    Positive integers which have an odd number of 1s in the binary expansions of their prime signatures
    OEIS A000379
    """

    for n,f in enumerate(prime_signatures(),1):
        total = 0
        for m in f:
            while m != 0:
                m,r = divmod(m,2)
                total ^= r
        if total == 0:
            yield n

if __name__ == '__main__':
    from Sequences.Manipulations import simple_test

    print("Primes")
    simple_test(primes(),15,
                "2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47")

    print("\nPrimorials")
    simple_test(primorial(),9,
                "1, 2, 6, 30, 210, 2310, 30030, 510510, 9699690")

    print("\nPrime Powers")
    simple_test(prime_powers(),16,
                "1, 2, 3, 4, 5, 7, 8, 9, 11, 13, 16, 17, 19, 23, 25, 27")

    print("\nPythagorean Primes")
    simple_test(pythagorean_primes(),13,
                "5, 13, 17, 29, 37, 41, 53, 61, 73, 89, 97, 101, 109")

    print("\nPrime Counting Function")
    simple_test(prime_counting(),18,
                "0, 0, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7")

    print("\nCharacteristic Function of the Primes")
    simple_test(prime_characteristic(),18,
                "0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0")

    print("\nSuperprimes")
    simple_test(superprimes(),13,
                "3, 5, 11, 17, 31, 41, 59, 67, 83, 109, 127, 157, 179")

    print("\nSophie Germain Primes")
    simple_test(sophie_germain_primes(),13,
                "2, 3, 5, 11, 23, 29, 41, 53, 83, 89, 113, 131, 173")

    print("\nSafe Primes")
    simple_test(safe_primes(),13,
                "5, 7, 11, 23, 47, 59, 83, 107, 167, 179, 227, 263, 347")

    print("\nTwin Primes")
    simple_test(twin_primes(),14,
                "3, 5, 7, 11, 13, 17, 19, 29, 31, 41, 43, 59, 61, 71")

    print("\nTwin Prime Pairs")
    simple_test(twin_prime_pairs(),6,
                "(3, 5), (5, 7), (11, 13), (17, 19), (29, 31), (41, 43)")

    print("\nSexy Prime Pairs")
    simple_test(n_gap_prime_pairs(6),5,
                "(5, 11), (7, 13), (11, 17), (13, 19), (17, 23)")

    print("\nPrime Tuples of form (0, 4, 6, 10, 12)")
    simple_test(prime_tuples((0, 4, 6, 10, 12)),2,
                "(7, 11, 13, 17, 19), (97, 101, 103, 107, 109)")

    print("\nPrime Gaps")
    simple_test(prime_gaps(),18,
                "1, 2, 2, 4, 2, 4, 2, 4, 6, 2, 6, 4, 2, 4, 6, 6, 2, 6")

    print("\nPrimes of form 3k+4")
    simple_test(linear_primes(3,4),14,
                "3, 7, 11, 19, 23, 31, 43, 47, 59, 67, 71, 79, 83, 103")

    print("\nBlum's Special Primes")
    simple_test(blum_primes(),10,
                "23, 47, 167, 359, 719, 1439, 2039, 2879, 4079, 4127")

    print("\n(3,5)-Prime Product Numbers")
    simple_test(prime_product([3,5]),13,
                "3, 5, 9, 15, 25, 27, 45, 75, 81, 125, 135, 225, 243")

    print("\n(3,5)-Prime Product Numbers (requiring both)")
    simple_test(prime_product([3,5],inclusive=True),11,
                "15, 45, 75, 135, 225, 375, 405, 675, 1125, 1215, 1875")

    print("\nPrime Signatures")
    simple_test(prime_signatures(),9,
                "(), (1,), (1,), (2,), (1,), (1, 1), (1,), (3,), (2,)")

    print("\nOdius Signature Numbers")
    simple_test(odius_signature(),16,
                "2, 3, 4, 5, 7, 9, 11, 13, 16, 17, 19, 23, 24, 25, 29, 30")

    print("\nEvil Signature Numbers")
    simple_test(evil_signature(),15,
                "1, 6, 8, 10, 12, 14, 15, 18, 20, 21, 22, 26, 27, 28, 32")


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

def newton_descent(f, gf, hf, x0, step_size, tolerance):
    """
    Args:
        f: function to minimize
        gf: gradient of f
        hf: hessian of f
        x0: initial x value
        step_size: step size
        tolerance: tolerance for stopping condition

    Returns:
        x_hist: history of x values as gradient descent updates x
        f_hist: history of f(x) values as gradient descent updates x
        iterations: number of iterations to reach tolerance
    """

    x_hist = []
    x_hist.append(x0)

    f_hist = []
    f_hist.append(f(x0))

    iterations = 0

    x = x0

    while True:
        inv_hf = np.linalg.inv(hf(x))
        x += step_size * -inv_hf @ gf(x)

        x_hist.append(x)
        f_hist.append(f(x))
        iterations += 1

        if abs(f_hist[-1] - f_hist[-2]) < tolerance:
            break

    return x_hist, f_hist, iterations

if __name__ == '__main__':

    def f(x: np.ndarray) -> float:
        """
        f(x) = x_1**2 + x_2**2 + x_3**2 ... + x_n**2
        """
        return np.linalg.norm(x)**2

    def gf(x: np.ndarray) -> np.ndarray:
        """
        gf(x) = 2 * x
        """
        return 2 * x

    def hf(x: np.ndarray) -> np.ndarray:
        """
        hf(x) = 2 * np.eye(n)
        """
        return 2 * np.eye(len(x))

    x0 = np.array([2., 4., -1., 1.])

    # def f(x: np.ndarray) -> float:
    #     Q = np.array([[2, 0, 0],
    #                   [0, 4, 0],
    #                   [0, 0, 6]])
    #     b = np.array([2, 2, 2])
    #     return 0.5 * (x.T @ Q @ x) - (b.T @ x)

    x0 = np.array([2., -2., 2])

    step_size = 1
    tolerance = 1e-5

    x_hist, f_hist, iterations = newton_descent(
        f, gf, hf, x0, step_size, tolerance)
    print(f'min x value: {x_hist[-1]}')
    print(f'min f value: {f_hist[-1]}')
    print(f'iterations to find minimum (within tolerance): {iterations-1}')

    # plot f_hist
    plt.plot(f_hist)
    plt.xlabel('iteration')
    plt.ylabel('$f(x)$')
    plt.title('Newton descent')
    plt.show()


import numpy as np

### Chú thích ###

# z là đầu vào hàm kích hoạt

### Các hàm kích hoạt ###

def linear(z, m):
	return m*z

def elu(z, alpha):
	return z if z >= 0 else alpha*(np.exp(z) - 1)

def leakyrelu(z, alpha):
	return max(alpha * z, z)

def relu(z):
  	return max(0, z)

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

def tanh(z):
	return (np.exp(z) - np.exp(-z)) / (np.exp(z) + np.exp(-z))

### Các đạo hàm ###

def linear_prime(z, m):
	return m

def elu_prime(z, alpha):
	return 1 if z > 0 else alpha*np.exp(z)

def leakyrelu_prime(z, alpha):
	return 1 if z > 0 else alpha

def sigmoid_prime(z):
  	return sigmoid(z) * (1-sigmoid(z))

def relu_prime(z):
  	return 1 if z > 0 else 0

def tanh_prime(z):
	return 1 - np.power(tanh(z), 2)