"""
Created by Joseph Edradan
Github: https://github.com/josephedradan

Date created: 12/28/2020

Purpose:
    Robert talked about a sequence equation solver and I thought it was easy so I made it...

Details:

Description:
    Solves the equation for the given sequence and assumes the x values list is relative to 0 and goes up though,
    you can explicitly make the x values list

Notes:
    This file uses matrices and numpy because it makes like easier...

IMPORTANT NOTES:

Explanation:
    Example:
        Given a sequence:
            [4, 14, 40, 88, 164]

        Find the differences between them and between them differences themselves as a list
            [10, 26, 48, 76]
            [16, 22, 28]
            [6, 6]
            [0]

        Select a degree based on the the list of differences with the same difference
            [6, 6]
            Degree == Row == Row index + 1 == 3

        Create the polynomial equation using degree 3
            (a)*(x)^3 + (b)*(x)^2 + (c)*(x)^1 + (d)*(x)^0 == y

        Solve for the coefficients by plugging in x values to equal the y values (y values are the in the sequence).
        x values are not stated so it is assumed that they start at 0 and to the length of the sequence.
        However, you can make your own x values list.

            Assuming that the x values list corresponds to the length of the sequence:
                [0, 1, 2, 3, 4]

                from

                [4, 14, 40, 88, 164]

            Now solve for the coefficients mathematically...

        Solve for the coefficients using a matrix instead:
            Recall the degree is 3 so the amount of rows in the matrix should be 3 + 1 == 4 because of
            coefficients a, b, c, d

            So

            [4, 14, 40, 88]

            based on

            [0, 1, 2, 3]

            Recall the equation:
                (a)*(x)^3 + (b)*(x)^2 + (c)*(x)^1 + (d)*(x)^0 == y

            Pre matrix:
                x = 0
                    a*(0)^3 + b*(0)^2 + c*(0)^1 + d*(0)^0 == 4
                x = 1
                    a*(1)^3 + b*(1)^2 + c*(1)^1 + d*(1)^0 == 14
                x = 2
                    a*(2)^3 + b*(2)^2 + c*(2)^1 + d*(2)^0 == 40
                x = 3
                    a*(3)^3 + b*(3)^2 + c*(3)^1 + d*(3)^0 == 88

            So the matrix representation is
                [[ 0  0  0  1]        [[a]        [[4]
                 [ 1  1  1  1]    .    [b]    =    [14]
                 [ 8  4  2  1]         [c]         [40]
                 [27  9  3  1]]        [d]]        [88]]

            Let represent the matrix in form: A . Z = B
            Now rearrange the matrix to form: A^-1 . B = Z
            Now solve for Z which will give you the values of the coefficients.

Reference:
    1st, 2nd and 3rd differences and Polynomials
        https://www.youtube.com/watch?v=PhcQvPITG3A
            Notes:
                Learning what differences are and how they relate to polynomials

    Determine Polynomial Equation From Table of Values Using Finite Difference
        https://www.youtube.com/watch?v=ORH3TwgvJgI
            Notes:
                How to find the differences and get create the polynomial equation

    What is a Coefficient Matrix, Augmented Matrix
        https://www.youtube.com/watch?v=BUSsrbMehPI
            Notes:
                Basically, we are trying to solve for a, b, c, d, ... instead of x if you watch this video.
                The below reference is a better example of what we are trying to do.

    Solving Linear Systems Using Matrices
        https://www.youtube.com/watch?v=C2QI3eeIiVc
            Notes:
                This is a better example of what we are trying to do.
                Basically, replace x, y, z, ... with a, b, c, ...

    Solve Linear Equations with Python
        https://www.youtube.com/watch?v=44pAWI7v5Zk
            Notes:
                How to solve linear equations in python using numpy

    Python 🐍 Solve Linear Equations
        https://www.youtube.com/watch?v=NO20jcoHH4I
            Notes:
                 The numpy functions to find the inverse matrix and do the dot product.
                 This is used to solve for the coefficients using a matrix
                    https://youtu.be/NO20jcoHH4I?t=167
"""
import math
from typing import List, Union

import numpy as np

EQUATION_FORMAT = "({:.2f})*({})^{} + "

def recursive_difference_solver(list_sequence: list, list_list_difference: List[List[int]] = None) -> List[List[int]]:
    """
    Find the finite differences

    :param list_sequence: list containing the sequence
    :param list_list_difference: list containing the differences
    :return: list_list_difference
    """

    # If there is no list_list_difference given then make one
    if list_list_difference is None:
        # List of differences
        list_list_difference = []

        # Append the given list_sequence to list_list_difference
        # list_list_difference.append(list_sequence)

    # If the current list_sequence is of size 1
    if len(list_sequence) == 1:
        # You can return nothing here as well!
        return list_list_difference

    # Make a temp difference list
    list_difference_temp = []

    # Loop through list_sequence comparing list_sequence[n] - list_sequence[n]
    for index in range(len(list_sequence) - 1):
        # Find the difference
        difference = list_sequence[index + 1] - list_sequence[index]

        # Append the difference to list_difference_temp
        list_difference_temp.append(difference)

    # Append list_difference_temp to list_list_difference
    list_list_difference.append(list_difference_temp)

    # Recursive call to find the other differences
    recursive_difference_solver(list_difference_temp, list_list_difference)

    # return the difference at end of the recursive call (eventually it will return to the caller)
    if len(list_list_difference[-1]) == 1:
        return list_list_difference

def find_row_index_with_same_values(list_list_difference: List[List]) -> int:
    """
    Find the row containing the similar values which determines the degree of the polynomial

    :param list_list_difference: list of differences
    :return: return the (index + 1) AKA the degree
    """
    for index, row in enumerate(list_list_difference):
        if len(set(row)) == 1:
            return index + 1

def get_polynomial_equation(degree: int, list_coefficient_values: Union[List, np.ndarray]) -> str:
    """
    Given the degree and the list_coefficient_values make the equation and return it as a string

    :param list_coefficient_values: the values of for the coefficients in the equation
    :param degree: the polynomial degree
    :return: return the equation
    """

    # Temp string of the polynomial
    equation_temp = ""

    """
    Loop through the degree and the coefficient value at the same time.
    Note that the degrees are reversed and that the reversed iterator is like a generator and can be used 1 time
    """
    for degree, coefficient_value in zip(reversed(range(0, degree + 1)), list_coefficient_values):
        equation_temp += EQUATION_FORMAT.format(coefficient_value, "x", degree)

    # Strip the space and the extra "+" at the end of the string
    equation_temp = equation_temp.strip().rstrip(" + ")

    return equation_temp

def get_x_values_matrix(degree: int, list_x_values: List[int] = None) -> List[List[int]]:
    """
    Get the matrix of the x values with their coefficients

    Notes:
        Ya i know that you can have any size list_x_values because the degree will be the limiter

    :param degree: degree of the polynomial
    :param list_x_values: list of x values that could be anything really...
    :return: matrix of the x values by their degree
    """

    # If you don't give a list_x_values then we'll just assume that it starts from 0 to the degree
    if list_x_values is None:
        list_x_values = list(range(degree + 1))

    # If the degree + 1 does not match the length of list_x_values
    if degree + 1 != len(list_x_values):
        print(
            "Degree + 1 and length of list_x_values do not match! x values: {}".format(str(list_x_values[degree + 1:])))
        # exit(1)

    # Temp Matrix
    matrix_temp = []

    # Note that reverse iterators get exhausting to make it into a list because reverse iterators are a 1 time use
    list_degrees_reversed = list(reversed(range(0, degree + 1)))

    # For value in list of x values
    for value in list_x_values:

        # Make a temp row
        row_temp = []

        # For degree in list of degrees revered
        for degree in list_degrees_reversed:
            # Append to row_temp the value^degree
            row_temp.append(value ** degree)

        # Add row_temp to the temp matrix
        matrix_temp.append(row_temp.copy())

    return matrix_temp

def check_if_solver_is_correct(equation_polynomial, matrix_column_coefficients, degree, list_x_values, list_sequence):
    """
    Check if sequence_equation_solver is correct

    :param equation_polynomial: The polynomial equation
    :param matrix_column_coefficients: column of coefficient values
    :param degree: degree of the polynomial
    :param list_x_values: list of x values for the given sequence
    :param list_sequence: sequence as a list
    :return:
    """
    list_degrees_reversed = list(reversed(range(degree + 1)))

    print("Polynomial Equation: {}".format(equation_polynomial))
    print("Checking if the values in list_sequence equal the Mathematical values...")
    print()
    for sequence_value, x_value in zip(list_sequence, list_x_values):

        # 1 liner arithmetic_expression_reduced_sum
        # arithmetic_expression_reduced_sum = sum([coefficient * (x_value ** degree) for coefficient, degree in
        #                                          zip(matrix_column_coefficients, list_degrees_reversed)])

        equation_temp = ""

        arithmetic_expression_reduced_sum = 0

        for coefficient, degree in zip(matrix_column_coefficients, list_degrees_reversed):
            equation_temp += EQUATION_FORMAT.format(coefficient, x_value, degree)

            arithmetic_expression_reduced_sum += coefficient * (x_value ** degree)

        # Strip the space and the extra "+" at the end of the string
        equation_temp = equation_temp.strip().rstrip(" + ")

        equation_temp += " = {}".format(arithmetic_expression_reduced_sum)
        print(equation_temp)
        print("Does Sequence value: {} == Arithmetic value: {} ?".format(sequence_value,
                                                                         arithmetic_expression_reduced_sum))
        print("Using math.isclose():", math.isclose(sequence_value, arithmetic_expression_reduced_sum))
        print("Using np.allclose():", np.allclose(sequence_value, arithmetic_expression_reduced_sum))

        print()

def sequence_equation_solver(list_sequence: List[int], list_x_values: List[int] = None):
    """
    Solve to find the sequence equation_polynomial

    :param list_sequence: List of the sequence
    :param list_x_values: list of x values
    :return: equation_polynomial
    """
    # Get the list of differences
    list_list_difference = recursive_difference_solver(list_sequence)

    print("List of differences:")
    for row in list_list_difference:
        print(row)
    print()

    row_index = find_row_index_with_same_values(list_list_difference)
    degree = row_index

    print("Find the row with the same difference value (row == degree):")
    print(row_index)
    print()

    # If you don't give a list_x_values then we'll just assume that it starts from 0 to the degree
    if list_x_values is None:
        list_x_values = list(range(degree + 1))

    # Create the x values matrix limited by the degree + 1
    matrix_x_values = get_x_values_matrix(degree, list_x_values[:degree + 1])

    print("Create the matrix of x values with their corresponding coefficient:")
    print(np.array(matrix_x_values))
    print()

    matrix_column_y = list_sequence[0:degree + 1]
    print("Create the Column matrix for the y values (Matches the size the degree + 1):")
    """
    Reference:
        Numpy print a 1d array as a column
            https://stackoverflow.com/questions/48724936/numpy-print-a-1d-array-as-a-column

    """
    print(np.array(matrix_column_y)[:, None])
    print()

    matrix_x_values_inverse = np.linalg.inv(matrix_x_values)
    print("Create the Inverse matrix of the x values:")
    print(matrix_x_values_inverse)
    print()

    matrix_column_coefficients = np.dot(matrix_x_values_inverse, matrix_column_y)
    print("The Dot product of the Inverse matrix of x values and the Column matrix for the y values, "
          "this gets the values of the coefficients:")
    print(matrix_column_coefficients)
    print()

    equation_polynomial = get_polynomial_equation(degree, matrix_column_coefficients)
    print("The Polynomial Equation:")
    print(equation_polynomial)
    print()

    return equation_polynomial, matrix_column_coefficients, degree, list_x_values, list_sequence

def test_example():
    # Example sequence
    list_sequence = [4, 14, 40, 88, 164]

    # This should be the length of the list as a list
    list_sequence_corresponding_x_values = list(range(len(list_sequence)))

    # Use this list to test any other values
    # list_sequence_corresponding_x_values = [-4, -2, 0, 2, 4]

    # Result of the solver
    result = sequence_equation_solver(list_sequence, list_sequence_corresponding_x_values)
    print("-" * 100)
    print()

    # Check if the solver is correct...
    check_if_solver_is_correct(*result)

if __name__ == '__main__':
    test_example()


"""Useful math utilities."""

import numpy as np

def softmax(x, t=1.0, axis=-1):
    x_ = x / t
    x_max = np.max(x_, axis=axis, keepdims=True)
    exps = np.exp(x_ - x_max)
    return exps / np.sum(exps, axis=axis, keepdims=True)

def log_softmax(x, t=1.0, axis=-1):
    x_ = x / t
    x_max = np.max(x_, axis=axis, keepdims=True)
    exps = np.exp(x_ - x_max)
    exp_sum = np.sum(exps, axis=axis, keepdims=True)
    return x_ - x_max - np.log(exp_sum)

def sigmoid(x):
    x = np.asarray(x)
    ret = np.zeros_like(x, dtype=float)
    ret[x > 0] = 1.0 / (1.0 + np.exp(-x[x > 0]))
    a = np.exp(x[x <= 0])
    ret[x <= 0] = a / (1.0 + a)
    return ret


import math
import numpy
import sympy

def function(x):
    return math.cos(x) + 3 * x + 1

def function_d(x):
    return 3 - math.sin(x)

def to_fixed(number):
    return f"{number:.{16}f}"

def results_output(results):
    print("---------------------------------------------------------")
    print("n \t| xn \t\t\t| f(xn) \t\t|")

    for i in range(len(results)):
        print("--------|-----------------------|-----------------------|")
        print(i, "\t|", results[i][0], "\t|", results[i][1], "\t|")

def dichotomy_method(a, b, eps):
    n = int(math.log2((b - a) / eps)) + 1
    results = []
    x = (a + b) / 2
    fx = function(x)

    results.append([x, to_fixed(fx)])

    if numpy.sign(a) == numpy.sign(fx):
        a = x
    else:
        b = x
    prev = x
    x = (a + b) / 2
    fx = function(x)
    results.append([x, to_fixed(fx)])

    while abs(x - prev) >= 2 * eps:
        if numpy.sign(a) == numpy.sign(fx):
            a = x
        else:
            b = x
        prev = x
        x = (a + b) / 2
        fx = function(x)
        results.append([x, to_fixed(fx)])

    print("-------------------Dichotomy Results---------------------")
    print("A priory mark: ", n)
    print("A posteriori mark: ", len(results))
    results_output(results)
    print("---------------------------------------------------------")

def find_max(x1, x2):
    maximum = 0
    i = x1
    while i < x2:
        maximum = function_d(i) if function_d(i) > maximum else maximum
        i += 0.001
    return maximum

def relaxation_method(a, b, eps):
    fda = abs(function_d(a))
    fdb = abs(function_d(b))
    min1 = fdb if fda > fdb else fda
    max1 = math.ceil(find_max(a, b))
    print("Min: ", min1, "Max: ", max1)
    x = a
    prev = 100
    z0 = abs(x)
    q = (max1 - min1)/(max1 + min1)
    t0 = 2/(max1 + min1)
    n = int(numpy.log(abs(z0)/eps)/numpy.log(1/q)) + 1
    fx = function(x)

    results = [[x, to_fixed(fx)]]
    while abs(prev - x) >= eps:
        prev = x
        x = prev - t0 * function(prev)
        fx = function(x)
        results.append([x, to_fixed(fx)])

    print("-------------------Relaxation Results---------------------")
    print("A priory mark: ", n)
    print("A posteriori mark: ", len(results))
    results_output(results)
    print("---------------------------------------------------------")

def main():
    a = -1*math.pi
    b = 0.5
    eps = 0.0001
    dichotomy_method(a, b, eps)
    relaxation_method(a, b, eps)

main()


# Question 03, Lab 05
# AB Satyaprakash - 180123062

# imports ----------------------------------------------------------------------------
from math import log, sin, pi
from sympy.abc import t
import numpy as np
import pandas as pd
import sympy as sp

# global dictionaries ----------------------------------------------------------------
phi = {-1: 0, 0: 1}
alpha = {}
beta = {0: 0}

# functions --------------------------------------------------------------------------

def simpsonRule(f, a, b, h):
    return ((h/3)*(f.subs(t, a)+4*f.subs(t, (a+b)/2)+f.subs(t, b))).evalf()

def compositeSimpsonRule(X):
    sum = 0
    a, b = X[0], X[-1]
    n = X.shape[0]
    h = (b-a)/(n-1)
    for i in range(X.shape[0]):
        x = X[i]
        if(i == 0 or i == n-1):
            sum += getFuncVal(x)
        else:
            if(i % 2 == 0):
                sum += 2*getFuncVal(x)
            else:
                sum += 4*getFuncVal(x)
    return (h*sum)/3

def getFuncVal(x):
    return 1/(1+x)

def getInnerProduct(a, b):
    poly = np.poly1d(np.polyint(np.polymul(a, b)))
    return poly(1) - poly(-1)

def getPhi(n):
    # The value of ϕ(n) = (x-α(n-1))ϕ(n-1) - β(n)ϕ(n-2)
    if n in phi:
        return phi[n]

    a = getAlpha(n-1)
    b = getBeta(n-1)
    phi_n_1 = getPhi(n-1)
    phi_n_2 = getPhi(n-2)

    secondTerm = np.polymul([b], phi_n_2)
    firstTerm = np.polymul(np.polysub([1, 0], a), phi_n_1)
    phi[n] = np.polysub(firstTerm, secondTerm)
    return phi[n]

def getAlpha(n):
    # The value of α(n) = <xϕ(n), ϕ(n)>/<ϕ(n) , ϕ(n)>
    if n in alpha:
        return alpha[n]

    phi_n = getPhi(n)
    xphi_n = np.polymul([1, 0], phi_n)
    num = getInnerProduct(xphi_n, phi_n)
    denom = getInnerProduct(phi_n, phi_n)
    alpha[n] = num/denom

    return alpha[n]

def getBeta(n):
    # The value of β(n) = <ϕ(n), ϕ(n)>/<ϕ(n-1) , ϕ(n-1)>
    if n in beta:
        return beta[n]

    phi_n = getPhi(n)
    phi_n_1 = getPhi(n-1)
    num = getInnerProduct(phi_n, phi_n)
    denom = getInnerProduct(phi_n_1, phi_n_1)
    beta[n] = num/denom

    return beta[n]

def getGaussian(roots, W, limits):
    a, b = limits
    I = 0
    for x, w in zip(roots, W):
        I += w*f(x, a, b)
    return I

def f(x, a, b):
    c1, c2 = (b-a)/2, (b+a)/2
    x = c1*x + c2
    return c1*getFuncVal(x)

# program body
# using 3 point Gaussian Quadrature rule!
n = 3
poly = np.poly1d(getPhi(n))
roots = poly.r
roots.sort()
temp = [[1] + [0]*(i) for i in range(n)]

output = []
for P in temp:
    P = (np.polyint(P))
    P = np.poly1d(P)
    I = P(1) - P(-1)
    output.append(I)

X = []
for i in range(n):
    temp = [x**i for x in roots]
    X.append(temp)
X_ = np.linalg.inv(X)
W = np.dot(X_, output)

# Given integral = ∫1/(1+x) from 0 to 1
limits = (0, 1)
result = getGaussian(roots, W, limits)
print("Using 3 point Gaussian Quadrature rule approx. value of integral is {}".format(result))

h = 0.125  # (given in question)
X = np.arange(0, 1+h/2, h)
simpsonValue = compositeSimpsonRule(X)
print("Using Simpson's 1/3 rule with h = {} approximate value of the integral is {}".format(h, simpsonValue))

func = 1/(1+t)
actualIntegratedValue = sp.integrate(func, (t, 0, 1)).evalf()
print("Actual value of integral is {}".format(
    actualIntegratedValue))

print('\n------------------------------------------------------------\n')
print("Error in estimation for Gaussain Quadrature rule = {}".format(
    abs(actualIntegratedValue-result)))
print("Error in estimation for Simpson's rule = {}".format(
    abs(actualIntegratedValue-simpsonValue)))

Semester 6/MA 322 (Scientific Computing Theory)/Labs/Lab 5/Code/q4.py


"""Is matrix a unitary matrix."""
import numpy as np

from toqito.matrix_props import is_square

def is_unitary(mat: np.ndarray, rtol: float = 1e-05, atol: float = 1e-08) -> bool:
    r"""
    Check if matrix is unitary [WikUnitary]_.

    A matrix is unitary if its inverse is equal to its conjugate transpose.

    Alternatively, a complex square matrix :math:`U` is unitary if its conjugate transpose
    :math:`U^*` is also its inverse, that is, if

    .. math::
        \begin{equation}
            U^* U = U U^* = \mathbb{I},
        \end{equation}

    where :math:`\mathbb{I}` is the identity matrix.

    Examples
    ==========

    Consider the following matrix

    .. math::
        X = \begin{pmatrix}
            0 & 1 \\
            1 & 0
            \end{pmatrix}

    our function indicates that this is indeed a unitary matrix.

    >>> from toqito.matrix_props import is_unitary
    >>> import numpy as np
    >>> A = np.array([[0, 1], [1, 0]])
    >>> is_unitary(A)
    True

    We may also use the `random_unitary` function from `toqito`, and can verify that a randomly
    generated matrix is unitary

    >>> from toqito.matrix_props import is_unitary
    >>> from toqito.random import random_unitary
    >>> mat = random_unitary(2)
    >>> is_unitary(mat)
    True

    Alternatively, the following example matrix :math:`B` defined as

    .. math::
        B = \begin{pmatrix}
            1 & 0 \\
            1 & 1
            \end{pmatrix}

    is not unitary.

    >>> from toqito.matrix_props import is_unitary
    >>> import numpy as np
    >>> B = np.array([[1, 0], [1, 1]])
    >>> is_unitary(B)
    False

    References
    ==========
    .. [WikUnitary] Wikipedia: Unitary matrix.
        https://en.wikipedia.org/wiki/Unitary_matrix

    :param mat: Matrix to check.
    :param rtol: The relative tolerance parameter (default 1e-05).
    :param atol: The absolute tolerance parameter (default 1e-08).
    :return: Return :code:`True` if matrix is unitary, and :code:`False` otherwise.
    """
    if not is_square(mat):
        return False

    uc_u_mat = mat.conj().T @ mat
    u_uc_mat = mat @ mat.conj().T
    id_mat = np.eye(len(mat))

    # If U^* * U = I U * U^*, the matrix "U" is unitary.
    return np.allclose(uc_u_mat, id_mat, rtol=rtol, atol=atol) and np.allclose(
        u_uc_mat, id_mat, rtol=rtol, atol=atol
    )


# -*- coding: utf-8 -*-
"""
Created on Sun Nov 22 21:08:04 2020

@author: franklin

https://aaronschlegel.me/black-scholes-formula-python.html
"""

import numpy as np
import scipy.stats as si
import itertools

def euro_vanilla(S, K, T, r, sigma, option = 'call'):

    #S: spot price
    #K: strike price
    #T: time to maturity
    #r: interest rate
    #sigma: volatility of underlying asset

    d1 = (np.log(S / K) + (r + 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))
    d2 = (np.log(S / K) + (r - 0.5 * sigma ** 2) * T) / (sigma * np.sqrt(T))

    if option == 'call':
        result = (S * si.norm.cdf(d1, 0.0, 1.0) - K * np.exp(-r * T) * si.norm.cdf(d2, 0.0, 1.0))
    if option == 'put':
        result = (K * np.exp(-r * T) * si.norm.cdf(-d2, 0.0, 1.0) - S * si.norm.cdf(-d1, 0.0, 1.0))

    return result

S, K, T, r, sigma = (50, 100, 1, 0.05, 0.25)
retCall = euro_vanilla(S, K, T, r, sigma)
retPut = euro_vanilla(S, K, T, r, sigma, option = 'put')
print(retCall)
print(retPut)

arrayS = list(range(1,101))
arraySigma = [x * 0.1 for x in range(1,101)]

#直積
#A={a, b, c}, B={d, e}
#A×B={(a,d),(a,e),(b,d),(b,e),(c,d),(c,e)}A×B={(a,d),(a,e),(b,d),(b,e),(c,d),(c,e)}
combi = list(itertools.product(arrayS, arraySigma))

def calc():
    results = []
    for S, sigma in combi:
        pv = euro_vanilla(S, K, T, r, sigma)
        # print(pv)
        results.append(pv)
    return results

#%timeit calc()
PVs = calc()

# import numpy as np
# output=np.array(combi)
# np.savetxt('combi.csv', output, delimiter=',', fmt='%f')


import numpy as np

def reg_poly(n, orientation):
    """Returns coordinates of a regular polygon

    Parameters
    ----------
    orientation: float
        The angle/orientation of the polygon

    n: positive integer
        number of sides

    Returns
    -------
    center: 1d numpy array of two elements
        center of the polygon
    vertices: list of 1d numpy arrays of two elements
        vertices of the polygon, going counterclockwise
    """
    # calculate n angles evenly spaces between zero and 2pi and then add the orientation
    angs = orientation + np.linspace(start=0, stop=2*np.pi*(n-1)/n, num=n)

    # define the center of the polygon as the origin
    center = np.array([0,0]).reshape(1,-1)

    # calculate x-y coords where each angle meets the unit circle
    vertices = np.concatenate( [np.cos(angs).reshape(-1,1), np.sin(angs).reshape(-1,1)], axis = 1 )
    return center, vertices

def iterate_shape(center, vertices, iters, scaling=0.5, rotation=0):
    """Replaces vertices of shape with scaled and
    rotated copies of the original shape. Can repeat
    process many times.

    Parameters
    ----------
    center: 1x2 numpy array
        The center of the original shape
    vertices: nx2 numpy array
        shape points
    iters: int
        number of times to repeat process
    scaling: float
        how much point-replacing copies
        of shape get scaled down from prev
        iteration
    rotation: float
        how much point-replacing copies
        of shape get rotated relative to
        prev iteration

    Returns
    -------
    all_coords: ?x2 numpy array
        array of new points in shape generated
        by applied the transformations
    """

    # initialize all_coords with the center
    all_coords = center.reshape(1,-1)
    # count number of vertices
    n_vertices = vertices.shape[0]

    # calculate vars for rotation matrix
    if rotation != 0:
        cos = np.cos(rotation)
        sin = np.sin(rotation)

    # for each iteration...
    for i in range(iters):
        # define an array to contain the next iteration of coordinates
        new_coords = np.ndarray(shape = (all_coords.shape[0]*n_vertices, 2))

        # rescale the vertices
        vertices = vertices * scaling
        # rotate the vertices if necessary
        if rotation != 0:
            vertices = np.array( [cos*vertices[:,0] - sin*vertices[:,1],
                                  sin*vertices[:,0] + cos*vertices[:,1]] ).T

        # create counter to record where in the new_coords array to input coords
        counter = 0

        # for every coordinate in all_coords,
        # record coords of a shape with vertices centered at that coordinate
        for coord in all_coords:
            new_coords[counter:(counter+n_vertices)] = coord + vertices
            counter += n_vertices

        # update all_coords with new_coords
        all_coords = new_coords
    return all_coords

def reg_poly_fractal(n_vertices, iters, scaling=0.5, rotation=0):
    """Produces iterated shape of regular
    polygon with n_vertices

    Parameters
    ----------
    n_vertices: int
        number of sides of regular polygon
    iters: int
        number of times to repeat process
    scaling: float
        how much point-replacing copies
        of shape get scaled down from prev
        iteration
    rotation: float
        how much point-replacing copies
        of shape get rotated relative to
        prev iteration

    Returns
    -------
    all_coords: ?x2 numpy array
        array of new points in shape generated
        by applied the transformations
    """
    c,v = reg_poly(n_vertices, np.pi/2)
    shape = iterate_shape(c, v, iters=iters, scaling=scaling, rotation=rotation)
    return shape


import numpy as np
from  scipy import sparse
import random
import networkx as nx
import matplotlib.pyplot as plt
from itertools import count
import time

Filename = "Wiki-Vote.txt"
NODES = 9000
EDGES = 103689

#function to compute Page Rank for the given adjacency matrix of the graph
#beta and epsilon is feeded into the function as constants
#return the number of iterations and rank matrix
def Estimate_PageRank(M, beta=0.85, epsilon=10**-4):

#as given matrix is adjacency matrix
#adjusting the matrix to get the stochastic adjacency matrix

	#calculating the outdegrees of each node with the help of sum function
	#incorporating even the beta value here i.e. di/beta
	#Transpose is needed as sum at axis=0 returns row vector but ranks are column vectors
	OutDegree_beta = M.sum(axis=0).T/beta
	n,_ = M.shape					#number of nodes
	ranks = np.ones((n,1))/n 		#Initial rank matrix to start the iterations i.e. 1/n
	iterations = 0					#number of iterations
	check = True					#Variable to loop the iteration

	#Declarations reqiured for Graph Visualisation:
	G=nx.DiGraph()					#Directed Graph using networkx for plotting the visualisations
	nodes=[]						#Stores the list of random nodes generated for plotting the graph
	size=[]							#List of sizes of the nodes which is scaled up factor of their ranks
	labels={}						#Labels of the nodes which is scaled up factor of their ranks

	#while loop for Page rank iterations
	while check:
	    iterations +=1
	    with np.errstate(divide='ignore'):				#Ignore division by 0 on ranks/OutDegree_beta
	    	#newranks=Beta*M*r(t-1)
	    	rank_next = M.dot((ranks/OutDegree_beta))	#Multiplying the ranks from previous iterations here
	        											#Injecting Stochastic behaviour by dividing rank with earlier computed outdegrees

	    #Next update ranks with (1-Beta)*r(t-1)/N

	    #rank_sum stores the matrix after the
	    rank_sum=np.ones((n,1))*ranks.sum()
	    rank_next=rank_next+rank_sum*(1-beta)/n

	    #Stop condition: if difference is less than epsilon then stop iterating
	    if np.linalg.norm(ranks-rank_next,ord=1)<=epsilon:
	        check = False
	    ranks = rank_next

	for x in range(100):					 #Generating 100 random nodes
		t=random.randint(1,8000)
		nodes.append(t)						 #appending the nodes in list
		size.append(10000000000*ranks[t])	 #storing their corresponding ranks as sizes
		labels[t]=(int)(10000000000*ranks[t])#labels as scaled up ranks

	for key in nodes:						 #adding the nodes and their corresponding edges in G garph
	 	G.add_node(key)
	 	for j in nodes:
	 		if M[j,key]==1:					 #edges are given by the adjacency matrix
	 			G.add_edge(key,j)
	'''
	#to remove isolated nodes from the graph
	for x in nx.isolates(G):
		G.remove_node(x)
		nodes.remove(x)
		size.remove(10000000000*ranks[x])
		del labels[x]
	'''

	nx.draw_random(G,node_list=nodes,node_size=size,labels=labels,with_labels=True)
	plt.show()
	return(ranks,iterations)

def sparse_Dic():
    with open(Filename,'r') as f:
        sp_dic_mat = sparse.dok_matrix((NODES,NODES),dtype=np.bool)
        for line in f.readlines()[4:]:
            from_node, to_node = (int(x)-1 for x in line.split())
            sp_dic_mat[to_node,from_node]=True
    return(sp_dic_mat.tocsr())

print('Estimating page rank:')
sp_csr=sparse_Dic()
start_time = time.time()
pr , iterate = Estimate_PageRank(sp_csr)
print('\nIterations: {0}'.format(iterate))
print('Node with maximum pagerank is: {0}'.format(np.argmax(pr)+1))
print("%s"% (time.time()-start_time))


# https://www.youtube.com/watch?v=ffSlRUmc7Ew

import numpy as np
import scipy.io.wavfile as wav

def makeWave(length, amp, freq, phase, rate=44100):
   input = np.arange(0.0, length, 1.0/rate)
   print("Sample Array Length and Values: "); print( len(input)) ; print(input)
   result = amp * np.sin(2*np.pi * freq * input + phase)
   return result

# In a second period what can we achieve

period = 1.0 # In seconds
pitchfrequency = 440.0 # Concert Pitch
amplitude = 1.0
phase = 0

# Case 1
# samplerate = 44100 # Sample per second
# wave = makeWave(period, amplitude, pitchfrequency, phase, samplerate)

# Case 2
# samplerate = 1000 # Sample per second
# period = 1.0 # In seconds
# phase = 0.5*np.pi # causing anti-aliasing
# wave = makeWave(period, amplitude, pitchfrequency, phase, samplerate)

# Case 3
# samplerate = 440 # Sample per second
# pitchfrequency = 294.0 # D3

# Case 4
samplerate = 440 # same sample rate as pitch frequency

# Case 5
# samplerate = 3 # Sample per second
# amplitude = 2.0

# Case 6
# samplerate = 10 # Sample per second

# Main Program
wave = makeWave(period, amplitude, pitchfrequency, phase, samplerate)
wav.write("test.wav", samplerate, wave)


import numpy as np
import matplotlib.pyplot as plt

# import tensorflow as tf
# from tensorflow.keras.layers import Layer
# from tensorflow.keras.initializers import RandomUniform, Initializer,

def gaussian_rbf(x, landmark, gamma=1):
    return np.exp(-gamma * np.linalg.norm(x - landmark) ** 2)

def end_to_end(X1, X2, ys, mu1, mu2):
    from_1 = [gaussian_rbf(i, mu1) for i in zip(X1, X2)]
    from_2 = [gaussian_rbf(i, mu2) for i in zip(X1, X2)]
    # plot

    plt.figure(figsize=(13, 5))
    plt.subplot(1, 2, 1)
    plt.scatter((x1[0], x1[3]), (x2[0], x2[3]), label="Class_0")
    plt.scatter((x1[1], x1[2]), (x2[1], x2[2]), label="Class_1")
    plt.xlabel("$X1$", fontsize=15)
    plt.ylabel("$X2$", fontsize=15)
    plt.title("Xor: Linearly Inseparable", fontsize=15)
    plt.legend()
    plt.subplot(1, 2, 2)
    plt.scatter(from_1[0], from_2[0], label="Class_0")
    plt.scatter(from_1[1], from_2[1], label="Class_1")
    plt.scatter(from_1[2], from_2[2], label="Class_1")
    plt.scatter(from_1[3], from_2[3], label="Class_0")
    plt.plot([0, 0.95], [0.95, 0], "k--")
    plt.annotate("Seperating hyperplane", xy=(0.4, 0.55), xytext=
    (0.55, 0.66),
                 arrowprops=dict(facecolor='black', shrink=0.05))
    plt.xlabel(f"$mu1$: {(mu1)}", fontsize=15)
    plt.ylabel(f"$mu2$: {(mu2)}", fontsize=15)
    plt.title("Transformed Inputs: Linearly Seperable", fontsize=15)
    plt.legend()
    # solving problem using matrices form
    # AW = Y
    A = []
    for i, j in zip(from_1, from_2):
        temp = []
        temp.append(i)
        temp.append(j)
        temp.append(1)
        A.append(temp)

    A = np.array(A)
    W = np.linalg.inv(A.T.dot(A)).dot(A.T).dot(ys)
    print(np.round(A.dot(W)))
    print(ys)
    print(f"Weights: {W}")
    return W

def predict_matrix(point, weights):
    gaussian_rbf_0 = gaussian_rbf(np.array(point), mu1)
    gaussian_rbf_1 = gaussian_rbf(np.array(point), mu2)
    A = np.array([gaussian_rbf_0, gaussian_rbf_1, 1])
    return np.round(A.dot(weights))

# points
x1 = np.array([0, 0, 1, 1])
x2 = np.array([0, 1, 0, 1])
ys = np.array([0, 1, 1, 0])
# centers
mu1 = np.array([0, 1])
mu2 = np.array([1, 0])
w = end_to_end(x1, x2, ys, mu1, mu2)
# testing
print(f"Input:{np.array([0, 0])}, Predicted:{predict_matrix(np.array([0, 0]), w)}")
print(f"Input:{np.array([0, 1])}, Predicted:{predict_matrix(np.array([0, 1]), w)}")
print(f"Input:{np.array([1, 0])}, Predicted:{predict_matrix(np.array([1, 0]), w)}")
print(f"Input:{np.array([1, 1])}, Predicted:{predict_matrix(np.array([1, 1]), w)}")


import scipy.integrate as spi
import numpy as np

print('Double integral computed by SciPy dblquad')
print('Example 2-01 dblquad')
print('Integral of 2xye^-xy from y=1 to y=5 and from x=y-1 to x=y+1')

integrand = lambda x, y : 2 * x * y * np.exp(-x * y)
ya = 1.
yb = 5.

result, error = spi.dblquad(integrand, ya, yb, lambda y: y-1, lambda y: y+1)
print('Result is ', result, 'with error ', error)


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Weighted residual methods in 1D

@author: Nicolas Guarin-Zapata
"""
from __future__ import division, print_function
from sympy import zeros, symbols, S
from sympy import integrate, diff, linsolve
from sympy import sin, pi

def least_squares(lhs, rhs, basis, nterms, domain=(0, 1)):
    """
    Return matrices for least squares solution of differential
    equations
    """
    x0, x1 = domain
    x = symbols("x")
    A_mat = zeros(nterms, nterms)
    b_vec = zeros(nterms, 1)
    for row in range(nterms):
        phi_i = basis(x, row)
        b_vec[row] = integrate(rhs(x)*lhs(phi_i, x), (x, x0, x1))
        for col in range(nterms):
            phi_j = basis(x, col)
            A_mat[row, col] = integrate(lhs(phi_i, x)*lhs(phi_j, x),
                                       (x, x0, x1))
    return A_mat, b_vec

def collocation(lhs, rhs, basis, nterms, domain=(0, 1), x_col=None):
    """
    Return matrices for least squares solution of differential
    equations
    """
    x0, x1 = domain
    if x_col is None:
        dx = S(x1 - x0)/(nterms - 2)
        x_col = [dx + dx*cont for cont in range(nterms)]
    x = symbols("x")
    A_mat = zeros(nterms, nterms)
    b_vec = zeros(nterms, 1)
    for row in range(nterms):
        b_vec[row] = rhs(x_col[row])
        for col in range(nterms):
            phi_j = basis(x, col)
            A_mat[row, col] = lhs(phi_j, x).subs(x, x_col[row])
    return A_mat, b_vec

def galerkin(lhs, rhs, basis, nterms, domain=(0, 1)):
    """
    Return matrices for Galerkin solution of differential
    equations
    """
    x0, x1 = domain
    x = symbols("x")
    A_mat = zeros(nterms, nterms)
    b_vec = zeros(nterms, 1)
    for row in range(nterms):
        phi_i = basis(x, row)
        b_vec[row] = integrate(rhs(x)*phi_i, (x, x0, x1))
        for col in range(nterms):
            phi_j = basis(x, col)
            A_mat[row, col] = integrate(lhs(phi_i, x)*phi_j, (x, x0, x1))
    return A_mat, b_vec

if __name__ == "__main__":
    basis = lambda x, k: x*(1 - x)*x**k
    lhs = lambda u, x: diff(u, x, 2) + u
    rhs = lambda x: -x
    x = symbols("x")
    u_e = sin(x)/sin(1) - x
    nterms = 10
    c = symbols('c0:%d' % nterms)
#    A, b = least_squares(lhs, rhs, basis, nterms)
#    A, b = collocation(lhs, rhs, basis, nterms)
    A, b =  galerkin(lhs, rhs, basis, nterms)
    sol = linsolve((A, b), c)
    subs = dict(zip(c, sol.args[0]))
    u = sum(c[cont]*basis(x, cont) for cont in range(nterms)).subs(subs)


"""

    This module calculates a linear system by Gaussian elimination with pivoting.
    Almost a copy of on Mike Zingale's code, spring 2013.

"""

import numpy as npy

def gaussElim(A, b):
    """ perform gaussian elimination with pivoting, solving A x = b A
        is an NxN matrix, x and b are an N-element vectors.  Note: A
        and b are changed upon exit to be in upper triangular (row
        echelon) form """

    # b is a vector
    if not b.ndim == 1:
        print "ERROR: b should be a vector"
        return None

    N = len(b)

    # A is square, with each dimension of length N
    if not (A.shape[0] == N and A.shape[1] == N):
        print "ERROR: A should be square with each dim of same length as b"
        return None

    # allocation the solution array
    x = npy.zeros((N), dtype=A.dtype)

    # find the scale factors for each row -- this is used when pivoting
    scales = npy.max(npy.abs(A), 1)

    # keep track of the number of times we swapped rows
    numRowSwap = 0

    # main loop over rows
    for k in range(N):

        # find the pivot row based on the size of column k -- only consider
        # the rows beyond the current row
        rowMax = npy.argmax(A[k:, k]/scales[k:])
        if (k > 0): rowMax += k  # we sliced A from k:, correct for total rows

        # swap the row with the largest scaled element in the current column
        # with the current row (pivot) -- do this with b too!
        if not rowMax == k:
            A[[k, rowMax],:] = A[[rowMax, k],:]
            b[[k, rowMax]] = b[[rowMax, k]]
            numRowSwap += 1

        # do the forward-elimination for all rows below the current
        for i in range(k+1, N):
            coeff = A[i,k]/A[k,k]

            for j in range(k+1, N):
                A[i,j] += -A[k,j]*coeff

            A[i,k] = 0.0
            b[i] += -coeff*b[k]

    # last solution is easy
    x[N-1] = b[N-1]/A[N-1,N-1]

    for i in reversed(range(N-1)):
        isum = b[i]
        for j in range(i+1,N):
            isum += -A[i,j]*x[j]
        x[i] = isum/A[i,i]

    return x


"""This file contains code used in "Think Stats",
by Allen B. Downey, available from greenteapress.com

Copyright 2014 Allen B. Downey
License: GNU GPLv3 http://www.gnu.org/licenses/gpl.html
"""

from __future__ import print_function

import numpy as np

import density
import hinc
import thinkplot
import thinkstats2

"""This file contains a solution to an exercise in Think Stats:

The distribution of income is famously skewed to the right.  In this
exercise, we'll measure how strong that skew is.
...

Compute the median, mean, skewness and Pearson's skewness of the
resulting sample.  What fraction of households reports a taxable
income below the mean?  How do the results depend on the assumed
upper bound?

My results with log_upper=6

mean 74278.7075312
std 93946.9299635
median 51226.4544789
skewness 4.94992024443
pearson skewness 0.736125801914
cdf[mean] 0.660005879567

With log_upper=7

mean 124267.397222
std 559608.501374
median 51226.4544789
skewness 11.6036902675
pearson skewness 0.391564509277
cdf[mean] 0.856563066521

With a higher upper bound, the moment-based skewness increases, as
expected.  Surprisingly, the Pearson skewness goes down!  The reason
seems to be that increasing the upper bound has a modest effect on the
mean, and a stronger effect on standard deviation.  Since std is in
the denominator with exponent 3, it has a stronger effect on the
result.

So this is apparently an example where Pearson skewness is not working
well as a summary statistic.  A better choice is a statistic that has
meaning in context, like the fraction of people with income below the
mean.  Or something like the Gini coefficient designed to quantify a
property of the distribution (like the relative difference we expect
between two random people).

"""

def InterpolateSample(df, log_upper=6.0):
    """Makes a sample of log10 household income.

    Assumes that log10 income is uniform in each range.

    df: DataFrame with columns income and freq
    log_upper: log10 of the assumed upper bound for the highest range

    returns: NumPy array of log10 household income
    """
    # compute the log10 of the upper bound for each range
    df['log_upper'] = np.log10(df.income)

    # get the lower bounds by shifting the upper bound and filling in
    # the first element
    df['log_lower'] = df.log_upper.shift(1)
    df.log_lower[0] = 3.0

    # plug in a value for the unknown upper bound of the highest range
    df.log_upper[41] = log_upper

    # use the freq column to generate the right number of values in
    # each range
    arrays = []
    for _, row in df.iterrows():
        vals = np.linspace(row.log_lower, row.log_upper, row.freq)
        arrays.append(vals)

    # collect the arrays into a single sample
    log_sample = np.concatenate(arrays)
    return log_sample

def main():
    df = hinc.ReadData()
    log_sample = InterpolateSample(df, log_upper=6.0)

    log_cdf = thinkstats2.Cdf(log_sample)
    thinkplot.Cdf(log_cdf)
    thinkplot.Show(xlabel='household income',
                   ylabel='CDF')

    sample = np.power(10, log_sample)
    mean, median = density.Summarize(sample)

    cdf = thinkstats2.Cdf(sample)
    print('cdf[mean]', cdf[mean])

    pdf = thinkstats2.EstimatedPdf(sample)
    thinkplot.Pdf(pdf)
    thinkplot.Show(xlabel='household income',
                   ylabel='PDF')

if __name__ == "__main__":
    main()

DSC 530 - Data Exploration and Analysis/ThinkStats2/code/chap06soln.py


# -*- coding: utf-8 -*-#
'''
# Name:         LeastSquare
# Description:  LeastSquare 最小二乘法
# Author:       super
# Date:         2020/5/8
'''

# 用最小二乘法得到解析解LSE

import numpy as np
import matplotlib.pyplot as plt
from pathlib import Path

from HelperClass.DataReader_1_0 import *

file_name = "../data/ch04.npz"

# 根据公式15
def method1(X,Y,m):
    x_mean = X.mean()
    p = sum(Y*(X-x_mean))
    q = sum(X*X) - sum(X)*sum(X)/m
    w = p/q
    return w

# 根据公式16
def method2(X,Y,m):
    x_mean = X.mean()
    y_mean = Y.mean()
    p = sum(X*(Y-y_mean))
    q = sum(X*X) - x_mean*sum(X)
    w = p/q
    return w

# 根据公式13
def method3(X,Y,m):
    p = m*sum(X*Y) - sum(X)*sum(Y)
    q = m*sum(X*X) - sum(X)*sum(X)
    w = p/q
    return w

# 根据公式14
def calculate_b_1(X,Y,w,m):
    b = sum(Y-w*X)/m
    return b

# 根据公式9
def calculate_b_2(X,Y,w):
    b = Y.mean() - w * X.mean()
    return b

if __name__ == '__main__':

    reader = DataReader_1_0(file_name)
    reader.ReadData()
    X,Y = reader.GetWholeTrainSamples()
    m = X.shape[0]
    w1 = method1(X,Y,m)
    b1 = calculate_b_1(X,Y,w1,m)

    w2 = method2(X,Y,m)
    b2 = calculate_b_2(X,Y,w2)

    w3 = method3(X,Y,m)
    b3 = calculate_b_1(X,Y,w3,m)

    print("w1=%f, b1=%f" % (w1,b1))
    print("w2=%f, b2=%f" % (w2,b2))
    print("w3=%f, b3=%f" % (w3,b3))


import math

import numpy as np

from .math_ import lcm
from .overrides import set_module
from .prime import prime_factors

__all__ = ["totatives", "euler_totient", "carmichael", "is_cyclic", "is_primitive_root", "primitive_root", "primitive_roots"]

@set_module("galois")
def totatives(n):
    """
    Returns the positive integers (totatives) in :math:`1 \\le k < n` that are coprime with :math:`n`,
    i.e. :math:`gcd(n, k) = 1`.

    The totatives of :math:`n` form the multiplicative group :math:`\\mathbb{Z}{_n^\\times}`.

    Parameters
    ----------
    n : int
        A positive integer.

    Returns
    -------
    list
        The totatives of :math:`n`.

    References
    ----------
    * https://en.wikipedia.org/wiki/Totative
    * https://oeis.org/A000010

    Examples
    --------
    .. ipython:: python

        n = 20
        totatives = galois.totatives(n); totatives
        phi = galois.euler_totient(n); phi
        len(totatives) == phi
    """
    if not isinstance(n, (int, np.integer)):
        raise TypeError(f"Argument `n` must be an integer, not {type(n)}.")
    if not n > 0:
        raise ValueError(f"Argument `n` must be a positive integer, not {n}.")

    if n == 1:
        return [0]
    else:
        return [t for t in range(1, n) if math.gcd(n, t) == 1]

@set_module("galois")
def euler_totient(n):
    """
    Counts the positive integers (totatives) in :math:`1 \\le k < n` that are relatively prime to
    :math:`n`, i.e. :math:`gcd(n, k) = 1`.

    Implements the Euler Totient function :math:`\\phi(n)`.

    Parameters
    ----------
    n : int
        A positive integer.

    Returns
    -------
    int
        The number of totatives that are relatively prime to :math:`n`.

    References
    ----------
    * https://en.wikipedia.org/wiki/Euler%27s_totient_function
    * https://oeis.org/A000010

    Examples
    --------
    .. ipython:: python

        n = 20
        phi = galois.euler_totient(n); phi

        # Find the totatives that are coprime with n
        totatives = [k for k in range(n) if math.gcd(k, n) == 1]; totatives

        # The number of totatives is phi
        len(totatives) == phi

        # For prime n, phi is always n-1
        galois.euler_totient(13)
    """
    if not isinstance(n, (int, np.integer)):
        raise TypeError(f"Argument `n` must be an integer, not {type(n)}.")
    if not n > 0:
        raise ValueError(f"Argument `n` must be a positive integer, not {n}.")

    if n == 1:
        return 1

    p, k = prime_factors(n)

    phi = 1
    for i in range(len(p)):
        phi *= p[i]**(k[i] - 1) * (p[i] - 1)

    return phi

def _carmichael_prime_power(p, k):
    if p == 2 and k > 2:
        return euler_totient(p**k) // 2
    else:
        return euler_totient(p**k)

@set_module("galois")
def carmichael(n):
    """
    Finds the smallest positive integer :math:`m` such that :math:`a^m \\equiv 1 (\\textrm{mod}\\ n)` for
    every integer :math:`a` in :math:`1 \\le a < n` that is coprime to :math:`n`.

    Implements the Carmichael function :math:`\\lambda(n)`.

    Parameters
    ----------
    n : int
        A positive integer.

    Returns
    -------
    int
        The smallest positive integer :math:`m` such that :math:`a^m \\equiv 1 (\\textrm{mod}\\ n)` for
        every :math:`a` in :math:`1 \\le a < n` that is coprime to :math:`n`.

    References
    ----------
    * https://en.wikipedia.org/wiki/Carmichael_function
    * https://oeis.org/A002322

    Examples
    --------
    .. ipython:: python

        n = 20
        lambda_ = galois.carmichael(n); lambda_

        # Find the totatives that are relatively coprime with n
        totatives = [i for i in range(n) if math.gcd(i, n) == 1]; totatives

        for a in totatives:
            result = pow(a, lambda_, n)
            print("{:2d}^{} = {} (mod {})".format(a, lambda_, result, n))

        # For prime n, phi and lambda are always n-1
        galois.euler_totient(13), galois.carmichael(13)
    """
    if not isinstance(n, (int, np.integer)):
        raise TypeError(f"Argument `n` must be an integer, not {type(n)}.")
    if not n > 0:
        raise ValueError(f"Argument `n` must be a positive integer, not {n}.")

    if n == 1:
        return 1

    p, k = prime_factors(n)

    lambdas = []
    for i in range(len(p)):
        lambdas.append(_carmichael_prime_power(p[i], k[i]))

    lambda_ = lcm(*lambdas)

    return lambda_

@set_module("galois")
def is_cyclic(n):
    """
    Determines whether the multiplicative group :math:`\\mathbb{Z}{_n^\\times}` is cyclic.

    The multiplicative group :math:`\\mathbb{Z}{_n^\\times}` is the set of positive integers :math:`1 \\le a < n`
    that are coprime with :math:`n`. :math:`\\mathbb{Z}{_n^\\times}` being cyclic means that some primitive root
    (or generator) :math:`g` can generate the group :math:`\\mathbb{Z}{_n^\\times} = \\{g, g^2, \\dots, g^k\\}`, where :math:`k` is order of the group.
    The order of the group is defined by Euler's totient function, :math:`\\phi(n) = k`. If :math:`\\mathbb{Z}{_n^\\times}` is cyclic,
    the number of primitive roots is found by :math:`\\phi(k)` or :math:`\\phi(\\phi(n))`.

    :math:`\\mathbb{Z}{_n^\\times}` is cyclic if and only if :math:`n` is :math:`2`, :math:`4`, :math:`p^k`, or :math:`2p^k`,
    where :math:`p` is an odd prime and :math:`k` is a positive integer.

    Parameters
    ----------
    n : int
        A positive integer.

    Returns
    -------
    bool
        `True` if the multiplicative group :math:`\\mathbb{Z}{_n^\\times}` is cyclic.

    References
    ----------
    * https://en.wikipedia.org/wiki/Primitive_root_modulo_n

    Examples
    --------
    The elements of :math:`\\mathbb{Z}{_n^\\times}` are the positive integers less than :math:`n` that are coprime with :math:`n`.
    For example when :math:`n = 14`, then :math:`\\mathbb{Z}{_{14}^\\times} = \\{1, 3, 5, 9, 11, 13\\}`.

    .. ipython:: python

        # n is of type 2*p^k, which is cyclic
        n = 14
        galois.is_cyclic(n)

        # The congruence class coprime with n
        Znx = set([a for a in range(1, n) if math.gcd(n, a) == 1]); Znx

        # Euler's totient function counts the "totatives", positive integers coprime with n
        phi = galois.euler_totient(n); phi

        len(Znx) == phi

        # The primitive roots are the elements in Znx that multiplicatively generate the group
        for a in Znx:
            span = set([pow(a, i, n) for i in range(1, phi + 1)])
            primitive_root = span == Znx
            print("Element: {:2d}, Span: {:<20}, Primitive root: {}".format(a, str(span), primitive_root))

        roots = galois.primitive_roots(n); roots

        # Euler's totient function phi(phi(n)) counts the primitive roots of n
        len(roots) == galois.euler_totient(phi)

    A counterexample is :math:`n = 15 = 3*5`, which doesn't fit the condition for cyclicness.
    :math:`\\mathbb{Z}{_{15}^\\times} = \\{1, 2, 4, 7, 8, 11, 13, 14\\}`.

    .. ipython:: python

        # n is of type p1^k1 * p2^k2, which is not cyclic
        n = 15
        galois.is_cyclic(n)

        # The congruence class coprime with n
        Znx = set([a for a in range(1, n) if math.gcd(n, a) == 1]); Znx

        # Euler's totient function counts the "totatives", positive integers coprime with n
        phi = galois.euler_totient(n); phi

        len(Znx) == phi

        # The primitive roots are the elements in Znx that multiplicatively generate the group
        for a in Znx:
            span = set([pow(a, i, n) for i in range(1, phi + 1)])
            primitive_root = span == Znx
            print("Element: {:2d}, Span: {:<13}, Primitive root: {}".format(a, str(span), primitive_root))

        roots = galois.primitive_roots(n); roots

        # Note the max order of any element is 4, not 8, which is Carmichael's lambda function
        galois.carmichael(n)
    """
    if not isinstance(n, (int, np.integer)):
        raise TypeError(f"Argument `n` must be an integer, not {type(n)}.")
    if not n > 0:
        raise ValueError(f"Argument `n` must be a positive integer, not {n}.")

    p, k = prime_factors(n)

    if n in [2, 4]:
        return True
    elif len(p) == 2 and 2 in p and k[p.index(2)] == 1:
        # n = 2 * p^k
        return True
    elif len(p) == 1 and p[0] != 2:
        # n = p^k
        return True
    else:
        # n does not represent a cyclic group
        return False

@set_module("galois")
def is_primitive_root(g, n):
    """
    Determines if :math:`g` is a primitive root modulo :math:`n`.

    :math:`g` is a primitive root if the totatives of :math:`n`, the positive integers :math:`1 \\le a < n`
    that are coprime with :math:`n`, can be generated by powers of :math:`g`.

    Parameters
    ----------
    g : int
        A positive integer that may be a primitive root modulo :math:`n`.
    n : int
        A positive integer.

    Returns
    -------
    bool
        `True` if :math:`g` is a primitive root modulo :math:`n`.

    Examples
    --------
    .. ipython:: python

        galois.is_primitive_root(2, 7)
        galois.is_primitive_root(3, 7)
        galois.primitive_roots(7)
    """
    if not isinstance(g, (int, np.integer)):
        raise TypeError(f"Argument `g` must be an integer, not {type(g)}.")
    if not isinstance(n, (int, np.integer)):
        raise TypeError(f"Argument `n` must be an integer, not {type(n)}.")
    if not n > 0:
        raise ValueError(f"Argument `n` must be a positive integer, not {n}.")
    if not 0 < g < n:
        raise ValueError(f"Argument `g` must be a positive integer less than `n`, not {g}.")

    if n == 2:
        # Euler totient of 2 is 1. We cannot compute the prime factorization of 1. There is only one
        # primitive root modulo 2 and it's 1.
        return g == 1

    phi = euler_totient(n)  # Number of non-zero elements in the multiplicative group Z/nZ
    primes, _ = prime_factors(phi)

    return pow(g, phi, n) == 1 and all(pow(g, phi // p, n) != 1 for p in primes)

@set_module("galois")
def primitive_root(n, start=1, stop=None, reverse=False):
    """
    Finds the smallest primitive root modulo :math:`n`.

    :math:`g` is a primitive root if the totatives of :math:`n`, the positive integers :math:`1 \\le a < n`
    that are coprime with :math:`n`, can be generated by powers of :math:`g`.

    Alternatively said, :math:`g` is a primitive root modulo :math:`n` if and only
    if :math:`g` is a generator of the multiplicative group of integers modulo :math:`n`, :math:`\\mathbb{Z}{_n^\\times}`.
    That is, :math:`\\mathbb{Z}{_n^\\times} = \\{g, g^2, \\dots, g^k\\}`, where :math:`k` is order of the group.
    The order of the group :math:`\\mathbb{Z}{_n^\\times}` is defined by Euler's totient function, :math:`\\phi(n) = k`.
    If :math:`\\mathbb{Z}{_n^\\times}` is cyclic, the number of primitive roots modulo :math:`n` is given by
    :math:`\\phi(k)` or :math:`\\phi(\\phi(n))`.

    See :obj:`galois.is_cyclic`.

    Parameters
    ----------
    n : int
        A positive integer.
    start : int, optional
        Starting value (inclusive) in the search for a primitive root. The default is `1`. The resulting primitive
        root, if found, will be :math:`\\textrm{start} \\le g < \\textrm{stop}`.
    stop : int, optional
        Stopping value (exclusive) in the search for a primitive root. The default is `None` which corresponds to `n`.
        The resulting primitive root, if found, will be :math:`\\textrm{start} \\le g < \\textrm{stop}`.
    reverse : bool, optional
        Search for a primitive root in reverse order, i.e. find the largest primitive root first. Default is `False`.

    Returns
    -------
    int
        The smallest primitive root modulo :math:`n`. Returns `None` if no primitive roots exist.

    References
    ----------
    * V. Shoup. Searching for primitive roots in finite fields. https://www.ams.org/journals/mcom/1992-58-197/S0025-5718-1992-1106981-9/S0025-5718-1992-1106981-9.pdf
    * L. K. Hua. On the least primitive root of a prime. https://www.ams.org/journals/bull/1942-48-10/S0002-9904-1942-07767-6/S0002-9904-1942-07767-6.pdf
    * https://en.wikipedia.org/wiki/Finite_field#Roots_of_unity
    * https://en.wikipedia.org/wiki/Primitive_root_modulo_n
    * http://www.numbertheory.org/courses/MP313/lectures/lecture7/page1.html

    Examples
    --------
    Here is an example with one primitive root, :math:`n = 6 = 2 * 3^1`, which fits the definition
    of cyclicness, see :obj:`galois.is_cyclic`. Because :math:`n = 6` is not prime, the primitive root
    isn't a multiplicative generator of :math:`\\mathbb{Z}/\\textbf{n}\\mathbb{Z}`.

    .. ipython:: python

        n = 6
        root = galois.primitive_root(n); root

        # The congruence class coprime with n
        Znx = set([a for a in range(1, n) if math.gcd(n, a) == 1]); Znx

        # Euler's totient function counts the "totatives", positive integers coprime with n
        phi = galois.euler_totient(n); phi

        len(Znx) == phi

        # The primitive roots are the elements in Znx that multiplicatively generate the group
        for a in Znx:
            span = set([pow(a, i, n) for i in range(1, phi + 1)])
            primitive_root = span == Znx
            print("Element: {}, Span: {:<6}, Primitive root: {}".format(a, str(span), primitive_root))

    Here is an example with two primitive roots, :math:`n = 7 = 7^1`, which fits the definition
    of cyclicness, see :obj:`galois.is_cyclic`. Since :math:`n = 7` is prime, the primitive root
    is a multiplicative generator of :math:`\\mathbb{Z}/\\textbf{n}\\mathbb{Z}`.

    .. ipython:: python

        n = 7
        root = galois.primitive_root(n); root

        # The congruence class coprime with n
        Znx = set([a for a in range(1, n) if math.gcd(n, a) == 1]); Znx

        # Euler's totient function counts the "totatives", positive integers coprime with n
        phi = galois.euler_totient(n); phi

        len(Znx) == phi

        # The primitive roots are the elements in Znx that multiplicatively generate the group
        for a in Znx:
            span = set([pow(a, i, n) for i in range(1, phi + 1)])
            primitive_root = span == Znx
            print("Element: {}, Span: {:<18}, Primitive root: {}".format(a, str(span), primitive_root))

    The algorithm is also efficient for very large :math:`n`.

    .. ipython:: python

        n = 1000000000000000035000061
        galois.primitive_root(n)
        galois.primitive_root(n, reverse=True)

    Here is a counterexample with no primitive roots, :math:`n = 8 = 2^3`, which does not fit the definition
    of cyclicness, see :obj:`galois.is_cyclic`.

    .. ipython:: python

        n = 8
        root = galois.primitive_root(n); root

        # The congruence class coprime with n
        Znx = set([a for a in range(1, n) if math.gcd(n, a) == 1]); Znx

        # Euler's totient function counts the "totatives", positive integers coprime with n
        phi = galois.euler_totient(n); phi

        len(Znx) == phi

        # Test all elements for being primitive roots. The powers of a primitive span the congruence classes mod n.
        for a in Znx:
            span = set([pow(a, i, n) for i in range(1, phi + 1)])
            primitive_root = span == Znx
            print("Element: {}, Span: {:<6}, Primitive root: {}".format(a, str(span), primitive_root))

        # Note the max order of any element is 2, not 4, which is Carmichael's lambda function
        galois.carmichael(n)
    """
    try:
        return next(_primitive_roots(n, start=start, stop=stop, reverse=reverse))
    except StopIteration:
        return None

@set_module("galois")
def primitive_roots(n, start=1, stop=None, reverse=False):
    """
    Finds all primitive roots modulo :math:`n`.

    :math:`g` is a primitive root if the totatives of :math:`n`, the positive integers :math:`1 \\le a < n`
    that are coprime with :math:`n`, can be generated by powers of :math:`g`.

    Alternatively said, :math:`g` is a primitive root modulo :math:`n` if and only
    if :math:`g` is a generator of the multiplicative group of integers modulo :math:`n`, :math:`\\mathbb{Z}{_n^\\times}`.
    That is, :math:`\\mathbb{Z}{_n^\\times} = \\{g, g^2, \\dots, g^k\\}`, where :math:`k` is order of the group.
    The order of the group :math:`\\mathbb{Z}{_n^\\times}` is defined by Euler's totient function, :math:`\\phi(n) = k`.
    If :math:`\\mathbb{Z}{_n^\\times}` is cyclic, the number of primitive roots modulo :math:`n` is given by
    :math:`\\phi(k)` or :math:`\\phi(\\phi(n))`.

    See :obj:`galois.is_cyclic`.

    Parameters
    ----------
    n : int
        A positive integer.
    start : int, optional
        Starting value (inclusive) in the search for a primitive root. The default is 1. The resulting primitive
        roots, if found, will be :math:`\\textrm{start} \\le x < \\textrm{stop}`.
    stop : int, optional
        Stopping value (exclusive) in the search for a primitive root. The default is `None` which corresponds to `n`.
        The resulting primitive roots, if found, will be :math:`\\textrm{start} \\le x < \\textrm{stop}`.
    reverse : bool, optional
        List all primitive roots in descending order, i.e. largest to smallest. Default is `False`.

    Returns
    -------
    list
        All the positive primitive :math:`n`-th roots of unity, :math:`x`.

    References
    ----------
    * V. Shoup. Searching for primitive roots in finite fields. https://www.ams.org/journals/mcom/1992-58-197/S0025-5718-1992-1106981-9/S0025-5718-1992-1106981-9.pdf
    * https://en.wikipedia.org/wiki/Finite_field#Roots_of_unity
    * https://en.wikipedia.org/wiki/Primitive_root_modulo_n
    * http://www.numbertheory.org/courses/MP313/lectures/lecture7/page1.html

    Examples
    --------
    Here is an example with one primitive root, :math:`n = 6 = 2 * 3^1`, which fits the definition
    of cyclicness, see :obj:`galois.is_cyclic`. Because :math:`n = 6` is not prime, the primitive root
    isn't a multiplicative generator of :math:`\\mathbb{Z}/\\textbf{n}\\mathbb{Z}`.

    .. ipython:: python

        n = 6
        roots = galois.primitive_roots(n); roots

        # The congruence class coprime with n
        Znx = set([a for a in range(1, n) if math.gcd(n, a) == 1]); Znx

        # Euler's totient function counts the "totatives", positive integers coprime with n
        phi = galois.euler_totient(n); phi

        len(Znx) == phi

        # Test all elements for being primitive roots. The powers of a primitive span the congruence classes mod n.
        for a in Znx:
            span = set([pow(a, i, n) for i in range(1, phi + 1)])
            primitive_root = span == Znx
            print("Element: {}, Span: {:<6}, Primitive root: {}".format(a, str(span), primitive_root))

        # Euler's totient function phi(phi(n)) counts the primitive roots of n
        len(roots) == galois.euler_totient(phi)

    Here is an example with two primitive roots, :math:`n = 7 = 7^1`, which fits the definition
    of cyclicness, see :obj:`galois.is_cyclic`. Since :math:`n = 7` is prime, the primitive root
    is a multiplicative generator of :math:`\\mathbb{Z}/\\textbf{n}\\mathbb{Z}`.

    .. ipython:: python

        n = 7
        roots = galois.primitive_roots(n); roots

        # The congruence class coprime with n
        Znx = set([a for a in range(1, n) if math.gcd(n, a) == 1]); Znx

        # Euler's totient function counts the "totatives", positive integers coprime with n
        phi = galois.euler_totient(n); phi

        len(Znx) == phi

        # Test all elements for being primitive roots. The powers of a primitive span the congruence classes mod n.
        for a in Znx:
            span = set([pow(a, i, n) for i in range(1, phi + 1)])
            primitive_root = span == Znx
            print("Element: {}, Span: {:<18}, Primitive root: {}".format(a, str(span), primitive_root))

        # Euler's totient function phi(phi(n)) counts the primitive roots of n
        len(roots) == galois.euler_totient(phi)

    The algorithm is also efficient for very large :math:`n`.

    .. ipython:: python

        n = 1000000000000000035000061

        # Euler's totient function phi(phi(n)) counts the primitive roots of n
        galois.euler_totient(galois.euler_totient(n))

        # Only find some of the primitive roots
        galois.primitive_roots(n, stop=100)

        # The generator can also be used in a for loop
        for r in galois.primitive_roots(n, stop=100):
            print(r, end=" ")

    Here is a counterexample with no primitive roots, :math:`n = 8 = 2^3`, which does not fit the definition
    of cyclicness, see :obj:`galois.is_cyclic`.

    .. ipython:: python

        n = 8
        roots = galois.primitive_roots(n); roots

        # The congruence class coprime with n
        Znx = set([a for a in range(1, n) if math.gcd(n, a) == 1]); Znx

        # Euler's totient function counts the "totatives", positive integers coprime with n
        phi = galois.euler_totient(n); phi

        len(Znx) == phi

        # Test all elements for being primitive roots. The powers of a primitive span the congruence classes mod n.
        for a in Znx:
            span = set([pow(a, i, n) for i in range(1, phi + 1)])
            primitive_root = span == Znx
            print("Element: {}, Span: {:<6}, Primitive root: {}".format(a, str(span), primitive_root))
    """
    return list(_primitive_roots(n, start=start, stop=stop, reverse=reverse))

def _primitive_roots(n, start=1, stop=None, reverse=False):
    # pylint: disable=too-many-branches
    if n in [1, 2]:
        yield n - 1
        return

    stop = n if stop is None else stop
    if not isinstance(n, (int, np.integer)):
        raise TypeError(f"Argument `n` must be an integer, not {type(n)}.")
    if not isinstance(start, (int, np.integer)):
        raise TypeError(f"Argument `start` must be an integer, not {type(start)}.")
    if not isinstance(stop, (int, np.integer)):
        raise TypeError(f"Argument `stop` must be an integer, not {type(stop)}.")
    if not isinstance(reverse, bool):
        raise TypeError(f"Argument `reverse` must be a bool, not {type(reverse)}.")
    if not 1 <= start < stop <= n:
        raise ValueError(f"Arguments must satisfy `1 <= start < stop <= n`, not `1 <= {start} < {stop} <= {n}`.")

    if not is_cyclic(n):
        return

    n = int(n)  # Needed for the pow() function
    phi = euler_totient(n)  # Number of non-zero elements in the multiplicative group Z/nZ
    primes, _ = prime_factors(phi)

    if phi == n - 1 or n % 2 == 1:
        # For prime n or odd n, must test all elements
        possible_roots = range(start, stop)
    else:
        # For even n, only have to test odd elements
        if start % 2 == 0:
            start += 1  # Make start odd
        possible_roots = range(start, stop, 2)

    if reverse:
        possible_roots = reversed(possible_roots)

    for r in possible_roots:
        if pow(r, phi, n) == 1 and all(pow(r, phi // p, n) != 1 for p in primes):
            yield r


import numpy
from matplotlib import pyplot

from numpy.polynomial.chebyshev import chebpts2

def collocation_matrix(n, bcs):
    x = 0.5 * (1 + chebpts2(n))
    b = numpy.zeros((n,))
    A = numpy.zeros((n,n))
    # Left boundary
    b[0] = bcs[0]
    A[0,0] = 1
    # Right boundary
    b[-1] = bcs[-1]
    A[-1, :] = 1

    for nx in range(1,n-1):
        for j in range(n):
            A[nx, j] = A[nx, j] - x[nx]**(j)
        for j in range(2,n):
            A[nx, j] = A[nx, j] + (j)*(j-1)*x[nx]**(j-2)

    return numpy.linalg.solve(A, b)

def compute_solution(coeffs, x):
    y = numpy.zeros_like(x)
    for j, c in enumerate(coeffs):
        y = y + c * x**j
    return y

x = numpy.linspace(0, 1, 1000)
bcs = [1, numpy.exp(1)]
c3 = collocation_matrix(3, bcs)
y3 = compute_solution(c3, x)
pyplot.figure()
pyplot.plot(x, y3, 'k--', x, numpy.exp(x), 'b-')
pyplot.figure()
pyplot.plot(x, y3-numpy.exp(x))

c5 = collocation_matrix(5, bcs)
y5 = compute_solution(c5, x)
pyplot.figure()
pyplot.plot(x, y5, 'k--', x, numpy.exp(x), 'b-')
pyplot.figure()
pyplot.plot(x, y5-numpy.exp(x))

nbasis = numpy.arange(3,15)
errors = numpy.zeros((len(nbasis),))
for ie, nb in enumerate(nbasis):
    c = collocation_matrix(nb, bcs)
    y = compute_solution(c, x)
    errors[ie] = abs(numpy.linalg.norm(y - numpy.exp(x)))
pyplot.figure()
pyplot.semilogy(nbasis, errors, 'kx', mew=2)


from __future__ import division
import numpy as np
from numpy import pi
from copy import copy
from scipy.spatial import ConvexHull

def cartesian_to_spherical(x, y, z, degrees=False):
    """
    Converts Cartesian coordinates to spherical coordinates
    cart = (x, y, z)
    sph = (r, theta, phi)   [Rad]

    >>> cartesian_to_spherical(1.0, 0.0, 0.0)
    (1.0, 1.5707963267948966, 0.0)
    >>> cartesian_to_spherical(0.0, 1.0, 0.0)
    (1.0, 1.5707963267948966, 1.5707963267948966)
    >>> cartesian_to_spherical(0.0, 0.0, 1.0)
    (1.0, 0.0, 0.0)
    """
    xsq, ysq, zsq = x*x, y*y, z*z
    r = (xsq + ysq + zsq)**0.5
    t = np.arctan2((xsq + ysq)**0.5, z)
    p = np.arctan2(y, x)
    if degrees:
        t, p = np.degrees(t), np.degrees(p)
    return r, t, p

def spherical_to_cartesian(r, t, p, degrees=False):
    if degrees:
        t, p = np.radians(t), np.radians(p)
    x = r * np.sin(t) * np.cos(p)
    y = r * np.sin(t) * np.sin(p)
    z = r * np.cos(t)
    return x, y, z

class Coordinates:
    """
    This class manages a list of coordinates.
    """

    def cart2sph(self):
        r, t, p = cartesian_to_spherical(self.x, self.y, self.z)
        # use our conventions, phi from 0 to 2*pi
        p = np.mod(p, 2*np.pi)
        return r, t, p

    def sph2cart(self, r, t, p):
        x, y, z = spherical_to_cartesian(np.ravel(r), np.ravel(t), np.ravel(p))
        self.cart = np.vstack((x, y, z)).T

    def __init__(self, cart=np.ndarray((0,3))):
        self._cart = np.array(cart)
        self.simplices = None

    def _get_cart(self):
        return self._cart
    def _set_cart(self, value):
        # reshape if the input is a 1d numpy array
        self._cart = value.reshape(-1,3)
    cart = property(_get_cart, _set_cart)

    def _get_x(self):
        return self.cart[:, 0]
    def _set_x(self, value):
        self.cart[:, 0] = value
    x = property(_get_x, _set_x)

    def _get_y(self):
        return self.cart[:,1]
    def _set_y(self, value):
        self.cart[:, 1] = value
    y = property(_get_y, _set_y)

    def _get_z(self):
        return self.cart[:, 2]
    def _set_z(self, value):
        self.cart[:, 2] = value
    z = property(_get_z, _set_z)

    def _get_r(self):
        r, t, p = self.cart2sph()
        return r
    def _set_r(self, value):
        r, t, p = self.cart2sph()
        r = np.array(value).ravel()
        self.sph2cart(r, t, p)
    r = property(_get_r, _set_r)

    def _get_theta(self):
        r, t, p = self.cart2sph()
        return t
    def _set_theta(self, value):
        r, t, p = self.cart2sph()
        t = np.array(value).ravel()
        self.sph2cart(r, t, p)
    theta = property(_get_theta, _set_theta)

    def _get_theta_deg(self):
        return self.theta * 180 / np.pi
    def _set_theta_deg(self, value):
        self.theta = value * np.pi / 180
    theta_deg = property(_get_theta_deg, _set_theta_deg)

    def _get_phi(self):
        r, t, p = self.cart2sph()
        return p
    def _set_phi(self, value):
        r, t, p = self.cart2sph()
        p = np.array(value).ravel()
        self.sph2cart(r, t, p)
    phi = property(_get_phi, _set_phi)

    def _get_phi_deg(self):
        return self.phi * 180 / np.pi
    def _set_phi_deg(self, value):
        self.phi = value * np.pi / 180
    phi_deg = property(_get_phi_deg, _set_phi_deg)

    def _get_sph(self):
        r, t, p = self.cart2sph()
        return np.vstack((r, t, p)).T
    def _set_sph(self, value):
        # reshape if the input is a 1d numpy array
        value = value.reshape(-1,3)
        if value.shape[0] != self.cart.shape[0]:
            # size does not match, initialize with zeros
            self.cart = np.zeros_like(value)
        try:
            r = value[:, 0]
            t = value[:, 1]
            p = value[:, 2]
            self.sph2cart(r, t, p)
        except IndexError:
            # still no data here, do nothing
            print('do nothing, no data here')
    sph = property(_get_sph, _set_sph)

    def _get_nPoints(self):
        return self.cart.shape[0]
    def _set_nPoints(self, value):
        self.cart = np.zeros((value,3))
    nPoints = property(_get_nPoints, _set_nPoints)

    def update_simplices(self):
        #simplices = np.ndarray((0,3))
        THETA_LIMIT = 10 / 180. * np.pi  # in rad
        DEVIATION_PERCENT = np.cos(THETA_LIMIT)

        if self.cart.shape[1] == 3 and self.cart.size > 0:

            # link: http://www.qhull.org/html/qh-optq.htm#QbB
            simplices = ConvexHull(points=self.cart, qhull_options='QbB').simplices

            grid = self.pull_on_unit_sphere()

            max_z = np.max(grid.z)
            min_z = np.min(grid.z)

            # open only gaps which are large
            if max_z > DEVIATION_PERCENT: max_z = 1.
            if min_z < -DEVIATION_PERCENT: min_z = -1.

            is_z_max = np.isclose(grid.z[simplices], max_z)
            is_z_min = np.isclose(grid.z[simplices], min_z)
            #is_z_min = self.z[simplices] == np.min(self.z)

            is_valid_min = np.sum(is_z_min, axis=1) < 3
            is_valid_max = np.sum(is_z_max, axis=1) < 3
            is_valid = np.logical_and(is_valid_min, is_valid_max)
            self.simplices = simplices[is_valid,:]

    def pull_on_unit_sphere(self):
        grid = copy(self)
        grid.cart = grid.cart / self.r[:, None] # pull on unit sphere
        return grid

if __name__=='__main__':
    import doctest
    doctest.testmod()


"""
Solve the predator prey or Lotka - Volterra equations.
"""

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

def compute_predator_prey_rhs(z, t, alpha, beta, gamma, delta):
    x, y = z
    dx_dt = alpha * x - beta * x * y
    dy_dt = delta * x * y - gamma * y
    return dx_dt, dy_dt

alpha = 2/3
beta = 4/3
gamma = 1
delta = 1
x0 = 0.8
y0 = 0.9
t_span = 40
args = (alpha, beta, gamma, delta)
z0 = (x0, y0)
t = np.linspace(0, t_span, 400)

if __name__ == "__main__":
    x, y = odeint(compute_predator_prey_rhs, z0, t, args=args).T
    plt.subplot(2, 1, 1)
    plt.plot(t, x, label="prey")
    plt.plot(t, y, label="predator")
    plt.xlabel("time t")
    plt.ylabel("popluation size")
    plt.title(r"Predator, Prey / Lotka - Volterra Model")
    plt.legend()
    plt.subplot(2, 1, 2)
    plt.plot(x, y)
    plt.title(r"$\alpha = %.2f, \beta = %.2f, \gamma = %.2f, \delta = %.2f$" % args)
    plt.xlabel("prey")
    plt.ylabel("predator")
    plt.tight_layout()
    plt.show()


import scipy.stats
import numpy as np

def f_test(sample_x, sample_y, larger_varx_alt):
    """
    Computes the F-value and corresponding p-value for a pair of samples and alternative hypothesis.

    Parameters
    ----------
    sample_x : list
        A random sample x1,...,xnx. Let its (underlying) variance be ox^2 and its sample variance Sx^2.
    sample_y : list
        A random sample y1,...,yny. Let its (underlying) variance be oy^2 and its sample variance Sy^2.
    larger_varx_alt : bool
        True if alternative hypothesis is ox^2 > oy^2. False if ox^2 < oy^2.

    Returns
    -------
    f_value : float
        Sx^2 / Sy^2 as defined in 'A Quick, Compact, Two-Sample Dispersion Test: Count Five'.
    p_value : float
        Let F be the F-distribution with nx, ny df. 1 - P(F < f_value) if larger_varx_alt = True, P(F < f_value) otherwise. More extreme F = Sx^2 / Sy^2 values for alternative ox^2 > oy^2 are to the right. More extreme F values for ox^2 < oy^2 are to the left.
    """

    # calculate unbiased sample variances (n-1 in the denominator)
    sample_var_x = np.var(sample_x, ddof=1)
    sample_var_y = np.var(sample_y, ddof=1)
    f_value = sample_var_x/sample_var_y
    nx = len(sample_x)
    ny = len(sample_y)

    # compute P(F < f_value) with nx-1, ny-1 df
    cdf = scipy.stats.f.cdf(f_value, nx-1, ny-1)

    # More extreme f_value = Sx^2 / Sy^2 values for alternative ox^2 > oy^2. ox^2 being even bigger would be represented by larger quotient Sx^2 / Sy^2.
    # More extreme f_value for ox^2 < oy^2 are to the left. ox^2 being even smaller would be represented by smaller quotient.
    p_value = 1 - cdf if larger_varx_alt else cdf
    return f_value, p_value

def f1_test(sample_x, sample_y, larger_varx_alt):
    """
    Computes the F1-value as defined in 'Fixing the F Test for Equal Variances' and corresponding p-value for a pair of samples and alternative hypothesis.

    Parameters
    ----------
    sample_x : list
        A random sample x1,...,xnx. Let its (underlying) variance be ox^2 and its sample variance Sx^2.
    sample_y : list
        A random sample y1,...,yny. Let its (underlying) variance be oy^2 and its sample variance Sy^2.
    larger_varx_alt : bool
        True if alternative hypothesis is ox^2 > oy^2. False if ox^2 < oy^2.

    Returns
    -------
    p_value : float
        Let F be the F-distribution with rx, ry df as specified in equation (1) of 'Fixing the F Test for Equal Variances'. 1 - P(F < f_value) if larger_varx_alt = True, P(F < f_value) otherwise.
    """

    # calculate unbiased sample variances (n-1 in the denominator)
    sample_var_x = np.var(sample_x, ddof=1)
    sample_var_y = np.var(sample_y, ddof=1)
    f_value = sample_var_x/sample_var_y
    nx = len(sample_x)
    ny = len(sample_y)
    xmean = np.mean(sample_x)
    ymean = np.mean(sample_y)

    # compute moment, variance below equatio (1) of Shoemaker paper
    fourth_moment = (np.sum((sample_x - xmean)**4) +
                     np.sum((sample_y - ymean)**4))/(nx + ny)
    pooled_var = ((nx-1)*sample_var_x + (ny-1)*sample_var_y)/(nx + ny)

    # see equation (1) of Shoemaker paper
    rx = 2*nx / ((fourth_moment/pooled_var**2) - ((nx - 3)/(nx - 1)))
    ry = 2*ny / ((fourth_moment/pooled_var**2) - ((ny - 3)/(ny - 1)))

    # compute P(F < f_value) with rx-1, ry-1 df
    cdf = scipy.stats.f.cdf(f_value, rx-1, ry-1)

    # More extreme f_value = Sx^2 / Sy^2 values for alternative ox^2 > oy^2. ox^2 being even bigger would be represented by larger quotient Sx^2 / Sy^2.
    # More extreme f_value for ox^2 < oy^2 are to the left. ox^2 being even smaller would be represented by smaller quotient.
    p_value = 1 - cdf if larger_varx_alt else cdf
    return p_value

def count_five(sample_x, sample_y, center):
    """
    Computes the extreme counts for samples x and y as defined in 'A Quick, Compact, Two-Sample Dispersion Test: Count Five'.

    Parameters
    ----------
    sample_x : list
        A random sample x1,...,xn.
    sample_y : list
        A random sample y1,...,ym.
    center : str
        Whether to use 'mean' or 'median' for centering.
    Returns
    -------
    extreme_count_x : int
        C_x computed with centering mu being sample mean if center = 'mean' and sample median if center = 'median' as defined in equation (1) of 'A Quick, Compact, Two-Sample Dispersion Test: Count Five'.
    extreme_count_y : int
        C_y defined analogously to C_x above.
    Raises
    ------
    ValueError
        If center is neither 'mean' or 'median'.
    """

    if center not in {'mean', 'median'}:
        raise ValueError('Invalid center %s' % (center))
    if center == 'mean':
        centering_x = np.mean(sample_x)
        centering_y = np.mean(sample_y)
    else:
        centering_x = np.median(sample_x)
        centering_y = np.median(sample_y)

    # compute absolute deviations from centering for x, y samples
    abs_dev_x = np.abs(np.array(sample_x) - centering_x)
    abs_dev_y = np.abs(np.array(sample_y) - centering_y)

    # count number of X deviations greater than max Y deviation and vice versa
    # see equation (1) of Count Five paper
    extreme_count_x = np.sum(np.where(abs_dev_x > np.max(abs_dev_y), 1, 0))
    extreme_count_y = np.sum(np.where(abs_dev_y > np.max(abs_dev_x), 1, 0))
    return extreme_count_x, extreme_count_y


import math
import matplotlib.pyplot as plt
import numpy as np
import matplotlib as mpl
mpl.rcParams['axes.unicode_minus']=False

def  sigmoid(x, T=1):
    return 1.0 / (1.0 + np.exp(-x/T))

fig = plt.figure(figsize=(6,4))
ax = fig.add_subplot(111)

x = np.linspace(-10, 10)
y_1 = sigmoid(x)
y_15 = sigmoid(x, 1.5)
y_2 = sigmoid(x, 2)
y_25 = sigmoid(x, 2.5)
y_3 = sigmoid(x, 3)
y_100 = sigmoid(x, 10)

plt.xlim(-11,11)
plt.ylim(-1.1,1.1)

ax.spines['top'].set_color('none')
ax.spines['right'].set_color('none')

ax.xaxis.set_ticks_position('bottom')
ax.spines['bottom'].set_position(('data',0))
ax.set_xticks([-10,-5,0,5,10])
ax.yaxis.set_ticks_position('left')
ax.spines['left'].set_position(('data',0))
ax.set_yticks([-1,-0.5,0.5,1])

plt.plot(x,y_1,label="T=1",color = "blue")
plt.plot(x,y_15,label="T=1.5", color = "red")
plt.plot(x,y_2,label="T=2", color = "green")
plt.plot(x,y_25,label="T=2.5", color = "pink")
plt.plot(x,y_3,label="T=3", color = "yellow")
plt.plot(x,y_100,label="T=10", color = "gray")
plt.legend()
plt.show()


import numpy as np

def OWA(X:np.ndarray, weights:np.ndarray) -> float:

	'''
		Yager's OWA
		Ordered Weighted Averaging
		- params
			- X : np.ndarray : set of values [n]
			- weights : np.ndarray : expert weights [n]
		- return:
			- value : float : result of the aggregation
	'''

	assert np.shape(X)[0] == np.shape(weights)[0],\
		('[Aggregation][OWA][ERROR]'
		 ' mismatch shape X:{} | weights:{}').format(np.shape(X),
		 											 np.shape(weights))

	assert np.sum(weights)== 1,\
		('[Aggregation][OWA][ERROR]'
		 ' sum of the weights vector must be 1. ({})'.format(np.sum(weights)))

	Xs = np.sort(X)[::-1]#descending order

	return np.sum(np.multiply(Xs,weights))

def OWA_T1(X:np.ndarray, kind:int=2) -> float:

	'''
		Ordegered Weight Averagins

		- Font:

		ZHOU, Shang-Ming et al. Type-1 OWA operators for
			aggregating uncertain information with uncertain
			weights induced by type-2 linguistic quantifiers.
			Fuzzy Sets and Systems, v. 159, n. 24, p. 3281-3296,
			2008.

		- params:
			- X : np.ndarray : input
			- kind : float : weights tendency
							 1 -> center values
							 2 -> higher values
							 3 -> lower values
		- return:
			- value : float : result of the aggregation
	'''

	assert kind in [1,2,3],\
		('[Aggregation][OWA_T1][ERROR] kind must be either'
		 ' 1, or 2, or 3. (given:{})'.format(kind))

	#Equation 4
	def Q(r:float, a:float, b:float)->float:
		if r<a:
			return 0
		elif r>=a and r<=b:
			return (r-a)/(b-a)
		else:#r>b
			return 1

	if kind == 1:
		a,b = 0.3, 0.8
	elif kind == 2:
		a,b = 0., 0.5
	else: #kind 2
		a,b = 0.5, 1.

	n = np.shape(X)[0]

	w = np.array([Q(r=i/n, a=a, b=b) - Q(r=(i-1)/n, a=a, b=b)\
					for i in range(1, n+1)])

	Xs = np.sort(X)[::-1]#descending order

	return np.sum(np.multiply(Xs, w))


from numpy import *
import numpy as np

pi180 =  pi/180
earth_radius = 6371.0*1000

def lldist(lon,lat):

    lat = lat * pi180
    dlon = (lon[1:] - lon[:-1])*pi180
    dlat = (lat[1:] - lat[:-1])

    a = (sin(dlat/2))**2 + cos(lat[:-1]) * cos(lat[1:]) * (sin(dlon/2))**2
    angles = lon * 0
    angles[1:] = 2 * arctan2( sqrt(a), sqrt(1-a) )
    return earth_radius * angles

def ll2dist(lon,lat):

    lat = lat * pi180
    dlon = (lon[1,:] - lon[0,:])*pi180
    dlat = (lat[1,:] - lat[0,:])

    a = (sin(dlat/2))**2 + cos(lat[0,:]) * cos(lat[1,:]) * (sin(dlon/2))**2
    angles = 2 * arctan2( sqrt(a), sqrt(1-a) )
    return earth_radius * angles

def ll2dist2vec(lonvec1, latvec1, lonvec2, latvec2):
    """Find distance between each respective elements in two sets of vectors"""
    latvec1 = np.deg2rad(latvec1)
    latvec2 = np.deg2rad(latvec2)
    lonvec1 = np.deg2rad(lonvec1)
    lonvec2 = np.deg2rad(lonvec2)

    dlon = (lonvec2 - lonvec1)
    dlat = (latvec2 - latvec1)

    a = (sin(dlat/2))**2 + cos(latvec1) * cos(latvec2) * (sin(dlon/2))**2
    angles = 2 * arctan2( sqrt(a), sqrt(1-a) )
    return earth_radius * angles

def spherical_dist(pos1, pos2, r=3958.75):
    pos1 = np.deg2rad(pos1)
    pos2 = np.deg2rad(pos2)
    cos_lat1 = np.cos(pos1[..., 0])
    cos_lat2 = np.cos(pos2[..., 0])
    cos_lat_d = np.cos(pos1[..., 0] - pos2[..., 0])
    cos_lon_d = np.cos(pos1[..., 1] - pos2[..., 1])
    return earth_radius * np.arccos(cos_lat_d - cos_lat1 *
                                    cos_lat2 * (1 - cos_lon_d))

def distance_matrix(lonvec, latvec):
    """Calculate a distance matrix for all combinations of the lat-lon positions"""
    latmat1,latmat2 = np.meshgrid(latvec, latvec)
    lonmat1,lonmat2 = np.meshgrid(lonvec, lonvec)
    pos1 = np.vstack((latmat1.flatten(), lonmat1.flatten())).T
    pos2 = np.vstack((latmat2.flatten(), lonmat2.flatten())).T

    dist =  spherical_dist(pos1,pos2)
    return dist.reshape(int(np.sqrt(len(dist))),-1)


import numpy as np
import pandas as pd
import sys
import os
import matplotlib.pyplot as plt
from matplotlib import style
import argparse

class KMeans:
    def __init__(self, k, tolerance, maxIterations):
        self.k = k #number of centroids
        self.tolerance = tolerance #threshold of centroid movement below which to stop
        self.maxIterations = maxIterations
    #data is a 2d array (pairs of coords)
    def fit(self, data):
        self.centroids = {}
        #set centroids to first k data points arbitrarily
        #ideally this would be random data points but this is ok
        for i in range(self.k):
            self.centroids[i] = data[i]
        for i in range(self.maxIterations):
            self.classifications = {}
            #on each iteration give each centroid an empty classification array
            for i in range(self.k):
                self.classifications[i] = []
            #for each data point calculate a distance from each centroid
            #classify each data point as belonging to the closest centroid
            #by adding it to that centroid's classification array
            for datapoint in data:
                    distances = [np.linalg.norm(datapoint-self.centroids[centroid]) for centroid in self.centroids]
                    classification = distances.index(min(distances))
                    self.classifications[classification].append(datapoint)
            prevCentroids = dict(self.centroids)
            #move each centroid to the average of the points classified as belonging to it
            for classification in self.classifications:
                if(len(self.classifications[classification])!=0):
                    self.centroids[classification] = np.average(self.classifications[classification], axis=0)
            optimized = True
            #for each centroid, compare the old and new centroids
            #if it moved less than the tolerance, keep 'optimized' bool true
            #otherwise, make it false
            for c in self.centroids:
                originalCentroid = prevCentroids[c]
                newCentroid = self.centroids[c]
                centroidMovement = np.sum((newCentroid-originalCentroid))
                centroidMoving = centroidMovement > self.tolerance
                if centroidMoving:
                    optimized = False
            #if all centroids are optimized we can stop iterating
            if optimized:
                break
    #return the predicted classification of any given point
    def predict(self, data):
        distances = [np.linalg.norm(data-self.centroids[centroid]) for centroid in self.centroids]
        classification = distances.index(min(distances))
        return classification

def readColumnsFromCSV(filepath, col1, col2, numRows):
    csvFrame = pd.read_csv(os.getcwd()+ "\\" + filepath)
    csvFrame = csvFrame.iloc[:,[col1, col2]]
    return csvFrame.head(min(len(csvFrame.index), numRows))

def main():
    parser=argparse.ArgumentParser(
        description='''K-means plotter for data from two user-specified columns in CSV format ''')
    parser.add_argument('-file', type=str, help='CSV filename in the working directory')
    parser.add_argument('-colx', type=int, help='0-based index of the x-axis column')
    parser.add_argument('-coly', type=int, help='0-based index of the y-axis column')
    parser.add_argument('-rows', type=int, default=100, help='Number of rows to take from head of CSV file. Default: 100')
    parser.add_argument('-tol', type=float, default=.01, help='Minimum centroid movement after maximization in order to continue iterating. Default: .01')
    parser.add_argument('-maxIter', type=int, default=300, help="Max iterations. Default:300")
    parser.add_argument('-k', type=int, default=2, help="Number of clusters to form. Default:2")
    args=parser.parse_args()

    df = readColumnsFromCSV(args.file, args.colx, args.coly, args.rows)

    kMeansClass = KMeans(args.k, args.tol, args.maxIter)
    kMeansClass.fit(df.values)

    colors = 10*["g","r","c","b","k"]

    #graph the data points as Xs colored w/r/t their centroid
    for classification in kMeansClass.classifications:
        color = colors[classification]
        for dataPoint in kMeansClass.classifications[classification]:
            plt.scatter(dataPoint[0], dataPoint[1], marker="x", color=color, s=150, linewidths=5)
    #graph the centroids as black circles
    for centroid in kMeansClass.centroids:
        x = kMeansClass.centroids[centroid][0]
        y = kMeansClass.centroids[centroid][1]
        plt.scatter(x, y,marker="o", color="k", s=150, linewidths=5)
    print("X axis:" + df.columns[0])
    print("Y axis:" + df.columns[1])
    plt.xlabel(df.columns[0])
    plt.ylabel(df.columns[1])
    plt.show()
main()


import numpy as np

from fastats.scaling import standard

def pearson(x, y):
    """
    Calculates the pearson r correlation to
    measure the degree of the relationship
    between two linearly related variables.

    If there are any NaN values in the data,
    the result will be NaN.

    Example
    -------

    >>> import numpy as np
    >>> x = np.array([1, 2, 3, 4])
    >>> y = np.array([2, 3, 4, 3])
    >>> pearson(x, y)
    0.63245553203367588

    :param x: a `numpy.array` of floats or ints
    :param y: a `numpy.array` of floats or ints
    :return: a float representing the correlation
    """
    assert x.shape == y.shape

    n = len(x)
    xy = x * y
    x2 = x ** 2
    y2 = y ** 2

    sum_x = np.sum(x)
    sum_y = np.sum(y)

    numer = n * np.sum(xy) - sum_x * sum_y

    first = n * np.sum(x2) - sum_x ** 2
    second = n * np.sum(y2) - sum_y ** 2
    denom = np.sqrt(first * second)
    return numer / denom

def pearson_pairwise(A):
    """
    Calculates the pearson r correlation to
    measure the degree of the relationship
    between pairs of columns of the supplied
    matrix A (similar to pandas.DataFrame.corr)
    """
    assert A.ndim > 1
    assert A.shape[1] > 1

    n = A.shape[0]
    A_std = standard(A)
    return (A_std.T @ A_std) / n

if __name__ == '__main__':
    import pytest
    pytest.main([__file__])


import numpy as np
import scipy.special as sps

def mu(N, p, values):
    """
    Evaluate pdf of Kravchuk base measure, equivalently the binomial B(N,p) pdf
    """
    if not np.all(np.ceil(values)==values):
        raise("IncorrectType")
    if not np.shape(values):
        values = np.array(values)
    res = np.zeros(values.shape, dtype='float')
    ind = (values>=0)
    res[ind] = np.exp( sps.gammaln(N+1) - sps.gammaln(values[ind]+1) -
        sps.gammaln(N-values[ind]+1) + values[ind]*np.log(p) + (N-values[ind])*np.log(1-p) )
    return res

def cmb(N, p, values, normalize=True):
    """
    Evaluate Conway-Maxwell-binomial CMB(N,p) pdf
    """
    if not np.all(np.ceil(values)==values):
        raise("IncorrectType")
    if not np.shape(values):
        values = np.array(values)
    res = np.zeros(values.shape, dtype='float')
    ind = (values>=0)
    res[ind] = np.exp( .5*sps.gammaln(N+1) - .5*sps.gammaln(values[ind]+1) -
        .5*sps.gammaln(N-values[ind]+1) + values[ind]*np.log(p) + (N-values[ind])*np.log(1-p) )
    if normalize:
        res /= np.max(res)
    return res

class KravchukPolynomials:
    """
    Iteratively evaluate Kravchuk polynomials using a three-term recurrence.

    Args:
        n -- the number of increasing degrees. Maximum degree will thus be n-1.
        N, p -- parameters of Kravchuk base measure B(N,p)
        values -- an array of values where to evaluate the polynomials.
    """

    def __init__(self, N, p, n, values):
        self.k = 1
        self.n = n
        self.N = N
        self.p = p
        self.values = values

        # Fill in the values of the first two unnormalized polynomials
        # Note we store two consecutive values since recurrence is of order 2
        self.res = np.ones((2, len(values)))
        self.res[1,:] += (values - p*N -1)

    def __iter__(self):
        return self

    def __next__(self):
        if self.k < self.n:
            k = self.k
            N = self.N
            p = self.p
            resTmp = self.res[0,:].copy()
            self.res[0,:] = self.res[1,:]
            self.res[1,:] = 1./(k+1)*( (self.values-
                (k+p*(N-2*k)))*self.res[1,:] -
                (N-k+1)*p*(1-p)*resTmp )
            self.k += 1
            return resTmp / np.exp(.5*( sps.gammaln(N+1)-sps.gammaln(k)-
                sps.gammaln(N-k+2)+(k-1)*np.log(p)+(k-1)*np.log(1-p) ))
        else:
            raise StopIteration()


#!/usr/bin/python
"""
File: Lagrange_poly1

Copyright (c) 2016 Michael Seaman

License: MIT

FROM ASSIGNMENT 2
Lagrange's interpolation model, modeling a continuous function
with a polynomial model
"""

import numpy as np
import math

def p_L(x, xp, yp):
    L_outputs = np.array([L_k(x, k, xp) for k in np.arange(len(xp))])
    return np.sum(yp * L_outputs)

def L_k(x, k, xp):
    k_index_removed = np.append(xp[:k],xp[k+1:])
    elements = (x - k_index_removed)/( float(xp[k]) - k_index_removed)
    return np.prod(elements)

def test_p_l(xp = np.linspace(0,math.pi, 5), yp = np.array([math.sin(y) for y in np.linspace(0,math.pi, 5)]) ):
    teststatus = True
    interpolatedList = np.copy(xp)
    for i in xrange(len(xp)):
        interpolatedList[i] = p_L(xp[i], xp, yp)
    for i in xrange(len(xp)):
        print interpolatedList[i], yp[i]
        if ( not (interpolatedList[i] < yp[i] + .005 and interpolatedList[i] > yp[i] - .0000005)):
            teststatus = False
    assert(teststatus)


# -*- coding: utf-8 -*-
"""
Created on Mon Sep 16 10:07:36 2019

@author: hamil
"""
import numpy as np
import matplotlib.pyplot as plt

#Step one create two arrays for x values and the sin of those x values
sinx_array = []
cosx_array = []
x_array = np.arange(-2*np.pi, 2*np.pi, 0.01)
for each_number in x_array:
    sin_value = np.sin(each_number)
    sinx_array.append(sin_value)
    cos_value = np.cos(each_number)
    cosx_array.append(cos_value)

#create a function to derive the sin(x)
def deriv_function(funct, x_array, h):
    y_array = []
    for new_values in x_array:
        value_H = funct(new_values + h)
        value = funct(new_values)
        nummer = value_H - value
        result = nummer / h
        y_array.append(result)

    return y_array, result

#Test one for sinx
value1,value2 = deriv_function(np.sin, x_array, 0.01)

#Calculate the gradient of the x values
value3 = np.gradient(sinx_array, x_array)

#Compare with the numpy function for the gradient
plt.plot(x_array, value3, color = "purple", label = "Python's Gradient Function")
plt.plot(x_array, value1, color = 'blue', label = "Forward Difference")
plt.plot(x_array, sinx_array, linestyle= 'dashed', color= 'gray', label= "Sin Function")
plt.plot(x_array, cosx_array, color = 'green', label= "Python's Cos Function")
plt.legend()
plt.title("Comparing Three functions for SinX First Derative")
plt.show()

#Determine the relative error for each function
absolute1 = np.subtract(value1, cosx_array)
absolute2 = np.subtract(value3, cosx_array)
forward_error = np.divide(absolute1,cosx_array)
gradient_error = np.divide(absolute2, cosx_array)

#Plot the errors
plt.errorbar(x_array, value3, yerr=gradient_error)
plt.errorbar(x_array, value1, yerr=forward_error)
plt.title("Relative Error for First Deratives")
plt.show()

#Now compute the second deratives using both ways and plot to compare
value5, value6 = deriv_function(np.cos, x_array,0.01)
cosx_array = np.negative(cosx_array)
neg_x_array = np.negative(x_array)
value7 = np.gradient(cosx_array, neg_x_array)
negsinx_array = np.negative(sinx_array)

#now comute the relative error for this group
absolute3 = np.subtract(value5, negsinx_array)
absolute4 = np.subtract(value7, negsinx_array)
forward_err = np.divide(absolute3, negsinx_array)
gradient_err = np.divide(absolute4,negsinx_array)

#Plot everything
plt.plot(x_array, value7, color = "purple", label = "Python's Gradient Function")
plt.plot(x_array, value5, color = 'blue', label = "Forward Difference")
plt.plot(x_array, negsinx_array, color= 'green', label= "Python's Neg. Sin Function")
plt.plot(x_array, cosx_array, linestyle = 'dashed', color = 'gray', label= "Cos Function")
plt.legend()
plt.title("Comparing Three functions for SinX Second Deratives")
plt.show()

plt.errorbar(x_array, value5, yerr = forward_err)
plt.errorbar(x_array, value7, yerr = gradient_err)
plt.title("Relative Error for Second Deratives")