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 IDONE
import numpy as np
import os
from scipy.optimize import rosen

def f(x, ub, lb):
	scaling = d * (100 * ((ub[0] - lb[0] ** 2) ** 2) + (ub[0] - 1) ** 2)
	result = rosen(x) / scaling
	return result

def test_Rosenbrock(d, max_evals=500):
	"""
	Optimize the Rosenbrock function
	:param d: number of dimensions
	:param max_evals: maximum number of evaluations
	:return:
	"""
	print(f"Testing IDONE on the {d}-dimensional Rosenbrock function with integer constraints.")
	print("The known global minimum is f(1,1,...,1)=0")
	lb = -5 * np.ones(d, dtype=int)  # Lower bound
	ub = 10 * np.ones(d, dtype=int)  # Upper bound
	x0 = np.round(np.random.rand(d) * (ub - lb) + lb)  # Random initial guess

	solX, solY, model, log = IDONE.minimize(f, x0, lb, ub, max_evals, args=(ub, lb))
	print("Solution found: ")
	print(f"X = {solX}")
	print(f"Y = {solY}")
	return solX, solY, model, log

d = 10  # Change this number to optimize the Rosenbrock function for different numbers of variables
solX_, solY_, model_, log_ = test_Rosenbrock(d, 1000)

# Visualise the results
log_.plot()


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()


import numpy as np
from sklearn.datasets import fetch_california_housing
from sklearn.preprocessing import StandardScaler
import tensorflow as tf

housing_data = fetch_california_housing()

#print(housing_data.data.shape)

m_,n=housing_data.data.shape

housing_data_plus_bias = np.c_[np.ones((m_,1)),housing_data.data]

scalar = StandardScaler()

#scalar.fit(housing_data_plus_bias)

scaled_housing_data_plus_bias = scalar.fit_transform(housing_data_plus_bias)

#print(scaled_housing_data_plus_bias)

# print(housing_data_plus_bias)

# print(housing_data_plus_bias.shape)

learning_rate=0.01
epochs = 1000

x= tf.constant(housing_data_plus_bias,dtype=tf.float32,name='x')
y= tf.constant(housing_data.target.reshape(-1,1) ,dtype=tf.float32,name='y')
x_t = tf.transpose(x)
#theta = tf.matmul(tf.matrix_inverse(tf.matmul(x_t,x)),tf.matmul(x_t,y))

#theta = tf.Variable(tf.random_uniform([n+1,1],-1.0,1.0),name="theta")

m = tf.Variable(tf.random_uniform([n+1,1],-1.0,1.0),name="m")
b = tf.Variable(tf.random_uniform([1,1],-1.0,1.0),name="b")

y_pred= tf.matmul(x,m)+b

error = tf.square(y_pred - y)
mse=tf.reduce_mean(error,name='mse')

#y_ = (tf.matmul(x,m)+b)

m_gradients = tf.Variable(-2/m_ * tf.matmul(x_t,y-y_pred))

b_gradients = tf.Variable((-2/m_) * tf.reduce_mean(y-y_pred))

# m_gradients_upd = tf.assign(m_gradients,m_gradients)

# b_gradients_upd = tf.assign(b_gradients,b_gradients)

m_ops = tf.assign(m, m- (learning_rate * m_gradients))
b_ops = tf.assign(b, b- (learning_rate * b_gradients))

init = tf.global_variables_initializer()

with tf.Session() as sess:
    sess.run(init)

    for i in range(epochs):
        if i % 100 == 0 :
            print("MSE = {}".format(mse.eval()))
        sess.run([m_ops,b_ops])

    print("Reduced MSE = {}".format(mse.eval()))
    print("Best m = {} ,b = {}".format(m.eval(),b.eval()))


"""
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 tensorflow as tf
import numpy as np

# ----- Vector Operations
## Note: no computation is performed here
# Declare variables, and give them a value
# The variables have not been initialized

# Initialize vector tensors. Note that they you need as many
# [] as the order of the tensor. The number of '[[' equates
# to the order of tensor
xpy = [[3, 5, 6]]
ypy = [[1, 2, 3]]

# ----- Constants  -------
# Investigate shape and rank
# Rank = 0
constLit = tf.constant(4.5, shape=()) # Still a tf.op.Tensor
# Rank = 1: [3.5, 5.6]
constA = tf.constant([3.5, 5.6], shape=(2,))
# Rank = 2: [[3.5, 5.6]]
constB = tf.constant([3.5, 5.6], shape=(1,2))
# Rank = 2: [[3.5,
#             5.6]]
constC = tf.constant([3.5, 5.6], shape=(2,1))

constMa = tf.constant([[1, 2], [9, 4]], shape=(2,2), dtype=tf.float32)
constMb = tf.constant([[5, 6], [7, 8]], shape=(2,2), dtype=tf.float32)

x = tf.Variable(xpy, dtype=tf.float32, name="x")
y = tf.Variable(ypy, dtype=tf.float32, name="y")

M = tf.concat(concat_dim=0, values=[x, y])

# Elementwise squaring
M2 = tf.square(M)

# Get the shape of a tensor as a list of two integers
shp = M.get_shape().as_list()

# Division
coeff_op = tf.div(tf.constant(1., dtype=tf.float32),
                  tf.constant(shp[0], dtype=tf.float32))

# Sum along a particular axis
sum_op = tf.reduce_sum(M2, axis=0)

# Multiplication
favg_man = tf.mul(coeff_op, sum_op)

# Get the mean across a particular axis of a 2+ dimension tensor
favg_tf = tf.reduce_mean(M2, axis=0)

# Add (addition) item by item
xyAddN = tf.add_n([x, y])

# Losses
dM = constMa - constMb
mse_loss = tf.contrib.losses.mean_squared_error(constMa, constMb)

####### Inverse Convolution ###############
batch_size = 5
input_channels = 128
output_channels = 96
output_shape = [batch_size, 100, 100, output_channels]
input_shape = [batch_size, 50, 50, input_channels]

strides = [1, 2, 2, 1]

inputSignal = tf.constant(0.1, shape=input_shape)
outputSignal = tf.constant(0.5, shape=output_shape)

W = tf.constant(0.1, shape=[4, 4, output_channels, input_channels])

forwardConv = tf.nn.conv2d(outputSignal, W, strides=strides, padding='SAME')

invConvOut = tf.nn.conv2d_transpose(inputSignal, W,
                                 output_shape=output_shape,
                                 strides=strides,
                                 padding='SAME')

init = tf.global_variables_initializer() # prepare an init node
with tf.Session() as sess:
    init.run() # actually initialize ALL the variables (variable.initializer.run())
    print('------- Variables, Tensors and Constants --------')
    print('constLit: {0}, \tshape: {1}, rank: {2}'.format(constLit.eval(), constLit.get_shape().as_list(), tf.rank(constLit).eval()))
    print('constA: {0}, \tshape: {1}, rank: {2}'.format(constA.eval(), constA.get_shape().as_list(), tf.rank(constA).eval()))
    print('constB: {0}, \tshape: {1}, rank: {2}'.format(constB.eval(), constB.get_shape().as_list(), tf.rank(constB).eval()))
    print('constC: {0}, \tshape: {1}, rank: {2}'.format(constC.eval(), constC.get_shape().as_list(),tf.rank(constC).eval()))

    print('\n------- Operations ------- ')
    print('Coeff_op: {0}'.format(coeff_op.eval()))
    print('Shape of x: {0}'.format(tf.shape(x).eval()))
    print('M: {0}'.format(M.eval()))
    print('M^2: {0}'.format(M2.eval()))
    print('Sum_op: {0}'.format(sum_op.eval()))
    print('Favg_man: {0}'.format(favg_man.eval()))
    print('Favg_tf: {0}'.format(favg_tf.eval()))
    print('XYAddN: {0}'.format(xyAddN.eval()))

    print('\n------- Losses ------- ')
    print('Delta Matrix {0}'.format(dM.eval()))
    print('MSE: {0}'.format(mse_loss.eval()))

    inputSignalVal = inputSignal.eval()
    outputSignalVal = outputSignal.eval()

    print('\n------- Forward Convolution ------- ')
    forwardConvVal = forwardConv.eval()
    print('Forward Conv Shape: {0}'.format(forwardConvVal.shape))
    print('\n------- Inverse Convolution ------- ')
    invConvOutVal = invConvOut.eval()
    print('Inv Conv Shape: {}'.format(invConvOutVal.shape))
    pass


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 pandas as pd
import random
from sklearn.datasets import make_blobs, make_regression

class ClusterData:
    '''
    Generates data for clustering with random number of clusters (in a given range).
    Args:
        n_samples - the number of samples (default = 1000)
        n_features - the number of features (default = 2)
        cluster_std - a cluster standard deviation (default = 1.0)
        centers_range - a range of random centroids number (default = (2, 10))
        random_state - a random state parameter for single 'centers_range' value
        return_labels - return label data or not (default = False)
    '''
    def __init__(self, n_samples=1000, n_features=2, cluster_std=1.0,
                 centers_range=(2, 10), random_state=None, return_labels=False):
        self.n_samples = n_samples
        self.n_features = n_features
        self.cluster_std = cluster_std
        self.centers_range = centers_range
        self.random_state = random_state
        self.return_labels = return_labels

    def generate(self, save=False, f_format='excel'):
        '''
        Args:
            save - save prediction in local directory or not
            f_format - format of data saving (if save = True): 'csv' or 'excel' (default)
        '''
        assert f_format in {'excel', 'csv'}
        centers = random.sample(range(self.centers_range[0], self.centers_range[1]), 1)[0]
        self.features, self.labels = make_blobs(n_samples = self.n_samples,
                                                n_features = self.n_features,
                                                centers = centers,
                                                cluster_std = self.cluster_std,
                                                random_state = self.random_state)

        if save == True and f_format == 'excel':
            writer = pd.ExcelWriter('ClusterData.xlsx', engine='xlsxwriter')
            pd.DataFrame(self.features).to_excel(writer, sheet_name='data')
            if self.return_labels == True:
                pd.DataFrame(self.labels).to_excel(writer, sheet_name='labels')
            writer.save()
        elif save == True and f_format == 'csv':
            pd.DataFrame(self.features).to_csv('ClusterData_features.csv')
            if self.return_labels == True:
                pd.DataFrame(self.labels).to_csv('ClusterData_labels.csv')
        else:
            pass

        if self.return_labels == False:
            return self.features
        elif self.return_labels == True:
            return self.features, self.labels
        else:
            raise ValueError('Please, set `return_labels` True or False')

class RegressionData:
    '''
    Generates data for regression.
    Args:
        n_samples - the number of samples (default = 1000)
        n_features - the number of features (default = 2)
        n_informative - the number of informative features (default = 2)
        n_targets - the number of regression targets (default = 1)
        bias - the bias term in the underlying linear model (default = 0.0)
        noise - the standard deviation of the gaussian noise applied to the output (default = 0.0)
        shuffle - shuffle the samples and the features (default = True)
        random_state - a random state parameter for single 'centers_range' value
        return_labels - return label data or not (default = False)
    '''

    def __init__(self, n_samples=1000, n_features=2, n_informative=2, n_targets=1,
                 bias=0.0, noise=0.0, shuffle=True, random_state=None, return_labels=True):
        self.n_samples = n_samples
        self.n_features = n_features
        self.n_informative = n_informative
        self.n_targets = n_targets
        self.bias = bias
        self.noise = noise
        self.shuffle = shuffle
        self.random_state = random_state
        self.return_labels = return_labels

    def generate(self, save=False, f_format='excel'):
        '''
        Args:
            save - save prediction in local directory or not
            f_format - format of data saving (if save = True): 'csv' or 'excel' (default)
        '''
        assert f_format in {'excel', 'csv'}
        self.features, self.labels = make_regression(n_samples=self.n_samples,
                                                     n_features=self.n_features,
                                                     n_informative = self.n_informative,
                                                     n_targets = self.n_targets,
                                                     bias = self.bias,
                                                     noise = self.noise,
                                                     shuffle = self.shuffle,
                                                     random_state=self.random_state)

        if save == True and f_format == 'excel':
            writer = pd.ExcelWriter('RegressionData.xlsx', engine='xlsxwriter')
            pd.DataFrame(self.features).to_excel(writer, sheet_name='data')
            if self.return_labels == True:
                pd.DataFrame(self.labels).to_excel(writer, sheet_name='labels')
            writer.save()
        elif save == True and f_format == 'csv':
            pd.DataFrame(self.features).to_csv('RegressionData_features.csv')
            if self.return_labels == True:
                pd.DataFrame(self.labels).to_csv('RegressionData_labels.csv')
        else:
            pass

        if self.return_labels == False:
            return self.features
        elif self.return_labels == True:
            return self.features, self.labels
        else:
            raise ValueError('Please, set `return_labels` True or False')


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")


import numpy as np
import scipy.stats as st

class EMH(object):

    # -----------------------------------
	# Homoskedastic Null Hypothesis
	# -----------------------------------

	def __mu(self, X, annualize:bool =True):
		"""
	    Given a log price process, this function estimates the value of mu_hat.
        Mu is the daily component of the returns which is attributable to upward,
        or downward drift.
	    This estimator correspond to the maximum-likelihood estimator of mu_hat
	    This estimate can be annualized.

	    Args:
	        X(array): A log price process.
	        annualize(boolean): Annualize the parameter estimate.

	    Returns:
	        muEst(float): The estimated value of mu.
		"""
		n = X.shape[0]
		if annualize:
			return ( 1 / n ) * ( X[-1] - X[0] ) * 252
		else:
			return ( 1 / n ) * ( X[-1] - X[0] )

	def __vol_a(self, X, q:int =1, unbiased:bool =True, annualize:bool =True):
		"""
		This function calculates 'the sample variance of the first-difference of
		X; it corresponds to the maximum likelihood-estimator of the variance
		parameter and therefore possesses the usal consistency, asymptotic
        normality and efficiency properties.'
        Andrew Lo, The Size and Power of the Variance Ratio Test

		Args:
			X(numpy array): A log pricess process
			q(int): aggregation value, used to calculate the qth differences.
			unbiased(boolean): the data represent a sample or the population?
			annualize(boolan): annualize the results

		Returns
			sigma_est(float): the sample variance of the first-difference of X

		"""
		# preallocating the data
		mu_est = self.__mu(X=X, annualize=False) # MLE for the drift component
		sigma_est = 0.0
		# total number of observations, this is consistent with the paper notation
		n = int(np.floor( X.shape[0] / q ))

		for k in range(1, n * q):
			sigma_est += ( X[k] - X[k - 1] - mu_est ) ** 2
			# sigma_est += ( X[k * q] - X[k * q - q] - ( q * mu_est ) ) ** 2

		# unbiasing the estimator
		if unbiased:
			sigma_est =  sigma_est / ( n * q - 1 )
		else:
			sigma_est = sigma_est / ( n * q )

		# annualizing the results
		if annualize:
			return np.sqrt( sigma_est * 252 )
		else:
			return sigma_est

	def __vol_b(self, X, q:int =1, unbiased:bool =True, annualize:bool =True):
		"""
		This function calculates the variance of qth diferences of Xt,
		which, under H1 (Homoskedastic variance increments), is q times
		the variance of first-differences. By dividing by q, we obtain
		the estimator sigma^2_b which also converges to sigma^2 under H1.

		Args:
			X(numpy array): series of prices
			q(int): aggregation value, used to calculate the qth differences.
			annualize(bool): annualization of the results.

		Results:
			sigma^2_b(float): variance of the qth difference.
		"""
		# pre-allocating the data
		mu_est = self.__mu(X=X, annualize=False)
		n = int(np.floor( X.shape[0] / q ))
		sigma_est = 0.0

		# differences of every qth observation
		for k in range(q, n * q): # q-1 BECAUSE python indexation
			sigma_est += ( X[k] - X[k - q] - (q * mu_est) ) ** 2

		# unbiasing the estimator
		if unbiased:
			m = q * (n * q - q + 1) * (1 - (q / ( n * q )))
			sigma_est = sigma_est / m
		else:
			sigma_est = sigma_est / ( n * ( q ** 2 ) )

		# annualizing the results
		if annualize:
			return np.sqrt( sigma_est * 252 )
		else:
			return sigma_est

	def __md(self, X, q:int, unbiased:bool =True, annualize:bool =True):
		"""
		differences in volatilities for both estimators. Under the null hypothesis
		of a Gaussian random walk, the tow estimators sigma^2_a and sigma^2_b should
		be close; therefore a test of the random walk may be constructed by computing
		the difference of both volatilities.

		Args:
			X(numpy array): array with the series of prices.
			q(int): aggregation value, used to calculate the qth differences.
			unsiased(boolean): is the user using a sample or the universe of
								observations?
			annualize(boolean): the results should be annualized?

		Results:
			Md(float): statistic for the volatilities differences.
		"""
		return self.__vol_b(X=X, q=q, unbiased=unbiased, annualize=annualize) - self.__vol_a(X=X, unbiased=unbiased, annualize=annualize)

	def __mr(self, X, q:int, unbiased:bool =True, annualize:bool =True):
		"""
		A test may also be based upon the dimensionless centered variance ratio
		Mr = sigma^2_b / sigma^2_a - 1. Which converges in probability to zero as well.
		So this method calculates this dimensionless ratio.

		Args:
			X(numpy array): array with the series of prices.
			q(int): aggregation value, used to calculate the qth differences.
			unsiased(boolean): is the user using a sample or the universe of
								observations?
			annualize(boolean): the results should be annualized?

		Results:
			Mr(float): statistic for the dimentionless volatilities.

		"""
		return ( self.__vol_b(X=X, q=q, unbiased=unbiased, annualize=annualize) / self.__vol_a(X=X, unbiased=unbiased, annualize=annualize) ) - 1

	def __h1(self, X, q:int, centered:bool =True, unbiased:bool =True, annualize:bool =True):
		"""
		This function calculates the IID Gaussian Null Hypothesis.
		The essence of the random walk hypothesis is the restriction that the disturbances
		e_t are serially correlated or that innovations are unforecastable from past innovations.
		Hence:
			H1: e_t IID N(0, sigma^2) / Homoskedasticity of variance increments

		Args:
			X(numpy array): series of prices to test
			q(int): aggregation value, used to calculate the qth differences.
			centered(bool): want a dimentionless statistic? (this is preferred by the author)
			unbiased(bool): the data is a sample or the universe?
			annualize(bool): annualization of the results.

		Returns:
			Variance test stadistic for Homokedastic variance increments.

		Notes:
		https://machinelearningmastery.com/critical-values-for-statistical-hypothesis-testing/
		https://stackoverflow.com/a/41338933/6509794
		"""
		n = np.floor( X.shape[0] / q )

		if centered:
			z1 =  np.sqrt( np.multiply(n, q) ) * self.__mr(X=X, q=q, unbiased=unbiased, annualize=annualize)
			asymp = ( 2 * ( 2 * q - 1 ) * ( q - 1 ) ) / ( 3 * q )
			return z1 * np.power(asymp, -0.5)
		else:
			z1 = np.sqrt( np.multiply(n, q) ) * self.__md(X=X, q=q, unbiased=unbiased, annualize=annualize)
			return z1

    # -----------------------------------
	# Heteroskedastic Null Hypothesis
	# -----------------------------------

	def __delta(self, X, q:int, j:int):
		"""
		 Helper method for the variance estimator v_hat

		"""
		# Get the estimate value for the drift component.
		mu_est = self.__mu(X, False)

		# preallocating the data
		n = int(np.floor( X.shape[0] / q ))

		# Estimate the asymptotice variance given q and j.
		# Because the numerator and denominator are separate calculations
		# two separate loops will going to take place
		# this is much more clear than one big loop

		# NUMERATOR case
		numerator = 0.0
		for k in range(j + 1, n * q):
			numerator += ( ( X[k] - X[k - 1] - mu_est ) ** 2 ) * ( ( X[k - j] - X[k - j - 1] - mu_est) ** 2 )

		# DENOMINATOR case
		denominator = 0.0
		for k in range(1, n * q):
			denominator += ( X[k] - X[k - 1] - mu_est ) ** 2

		# Compute and return the statistic
		return ( n * q * numerator ) / ( denominator ** 2 )

	def __v_hat(self, X, q):
		"""
		Estimator for the value of the asymptotic variance of the __mr statistic.
		This is equivalent to a weighted sum of the asymptotic variances for each of
		the autocorrelation co-efficients under the null hypothesis.

		Given a log price process, X, and a sampling interval, q, this
		method is used to estimate the asymptoticvariance of the __mr statistic in the
		presence of stochastic volatility.
		In other words, it is a heteroskedasticity consistent estimator of
		the variance of the __mr statistic. This parameter is used to estimate the
		probability that the given log price process was generated by a
		Brownian Motion model with drift and stochastic volatility.

		Args:
			X(numpy array): serie of log prices
			q(int): aggregation value, used to calculate the qth differences.

		Returns
			volatility(float): Estimator for the value of the asymptotic
							  variance of the __mr statistic.
		"""
		v_hat = 0.0
		for j in range(1, q - 1):
			delta = self.__delta(X=X, q=q, j=j)
			v_hat += ((( 2 * ( q - j ) ) / q ) ** 2 ) * delta

		return v_hat

	def __h2(self, X, q:int, centered:bool =True, unbiased:bool =True, annualize:bool =True):
		"""
		The Heteroskedastic Null Hypothesis
		"""
		n = np.floor( X.shape[0] / q )
		if centered:
			z2 = np.sqrt( np.multiply(n, q) ) * self.__mr(X=X, q=q, unbiased=unbiased, annualize=annualize)
			return z2 * np.power(self.__v_hat(X=X, q=q), -0.5)

		else:
			return None

    # -----------------------------------
	# The Variance Test Ratio Test
	# -----------------------------------

	def vrt(self, X, q:int, heteroskedastic:bool =True, centered:bool =True, unbiased:bool =True, annualize:bool =True):
		"""
		Calculate the z statistic and the p-value for the Varaciance Ratio test
		https://stackoverflow.com/a/20871775/6509794
		"""
		if heteroskedastic:
			z_score = self.__h2(X=X, q=q, centered=centered, unbiased=unbiased, annualize=annualize)
			p_value = 1 - st.norm.cdf(z_score)
			return z_score, p_value
		else:
			z_score = self.__h1(X=X, q=q, centered=centered, unbiased=unbiased, annualize=annualize)
			p_value = 1 - st.norm.cdf(z_score)
			return z_score, p_value


import math
from itertools import combinations

import numpy as np

from .overrides import set_module
from .math_ import prod

__all__ = ["gcd", "crt"]

@set_module("galois")
def gcd(a, b):
    """
    Finds the integer multiplicands of :math:`a` and :math:`b` such that :math:`a x + b y = \\mathrm{gcd}(a, b)`.

    This function implements the Extended Euclidean Algorithm.

    Parameters
    ----------
    a : int
        Any integer.
    b : int
        Any integer.

    Returns
    -------
    int
        Greatest common divisor of :math:`a` and :math:`b`.
    int
        Integer :math:`x`, such that :math:`a x + b y = \\mathrm{gcd}(a, b)`.
    int
        Integer :math:`y`, such that :math:`a x + b y = \\mathrm{gcd}(a, b)`.

    References
    ----------
    * T. Moon, "Error Correction Coding", Section 5.2.2: The Euclidean Algorithm and Euclidean Domains, p. 181
    * https://en.wikipedia.org/wiki/Euclidean_algorithm#Extended_Euclidean_algorithm

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

        a = 2
        b = 13
        gcd, x, y = galois.gcd(a, b)
        gcd, x, y
        a*x + b*y == gcd
    """
    if not isinstance(a, (int, np.integer)):
        raise TypeError(f"Argument `a` must be an integer, not {type(a)}.")
    if not isinstance(b, (int, np.integer)):
        raise TypeError(f"Argument `b` must be an integer, not {type(b)}.")

    r2, r1 = a, b
    s2, s1 = 1, 0
    t2, t1 = 0, 1

    while r1 != 0:
        q = r2 // r1
        r2, r1 = r1, r2 - q*r1
        s2, s1 = s1, s2 - q*s1
        t2, t1 = t1, t2 - q*t1

    return r2, s2, t2

@set_module("galois")
def crt(a, m):
    """
    Solves the simultaneous system of congruences for :math:`x`.

    This function implements the Chinese Remainder Theorem.

    .. math::
        x &\\equiv a_1\\ (\\textrm{mod}\\ m_1)

        x &\\equiv a_2\\ (\\textrm{mod}\\ m_2)

        x &\\equiv \\ldots

        x &\\equiv a_n\\ (\\textrm{mod}\\ m_n)

    Parameters
    ----------
    a : array_like
        The integer remainders :math:`a_i`.
    m : array_like
        The integer modulii :math:`m_i`.

    Returns
    -------
    int
        The simultaneous solution :math:`x` to the system of congruences.

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

        a = [0, 3, 4]
        m = [3, 4, 5]
        x = galois.crt(a, m); x

        for i in range(len(a)):
            ai = x % m[i]
            print(f"{x} = {ai} (mod {m[i]}), Valid congruence: {ai == a[i]}")
    """
    if not len(a) == len(m):
        raise ValueError(f"Arguments `a` and `m` are not the same length, {len(a)} != {len(m)}.")
    for pair in combinations(m, 2):
        if not math.gcd(pair[0], pair[1]) == 1:
            raise ValueError(f"Elements of argument `m` must be pairwise coprime, {pair} are not.")

    # Iterate through the system of congruences reducing a pair of congruences into a
    # single one. The answer to the final congruence solves all the congruences.
    a1, m1 = a[0], m[0]
    for a2, m2 in zip(a[1:], m[1:]):
        # Use the Extended Euclidean Algorithm to determine: b1*m1 + b2*m2 = 1,
        # where 1 is the GCD(m1, m2) because m1 and m2 are pairwise relatively coprime
        b1, b2 = gcd(m1, m2)[1:]

        # Compute x through explicit construction
        x = a1*b2*m2 + a2*b1*m1

        m1 = m1 * m2  # The new modulus
        a1 = x % m1  # The new equivalent remainder

    # Align x to be within [0, prod(m))
    x = x % prod(m)

    return x


import numpy as np
import matplotlib.pyplot as plt

from sklearn.datasets import load_iris
from sklearn.manifold import MDS

def calculate_dist(X):
    """
    @ param X: matrix
    @ return dist: The square of the distance between any two points
    """

    sum_x = np.sum(np.square(X), 1)
    # dist_ij^2=b_ii + b_jj - 2 * b_ij
    # sum_X as b_ii
    # sum_X.T as b_jj
    dist_ij = np.add(np.add( -2 * np.dot(X, X.T), sum_x).T, sum_x)

    return dist_ij

def Personal_MDS(data, n_dims):
    """

    @ param data: (n_samples, n_features)
    @ param n_dims: target n_dims

    @ return Z: (n_samples, n_dims)

    """

    n,d = data.shape
    dist_ij = calculate_dist(data)
    dist_ij[dist_ij < 0] = 0

    dist = np.ones((n,n)) * np.sum(dist_ij)/n**2
    dist_i = np.sum(dist_ij, axis=1, keepdims=True)/n
    dist_j = np.sum(dist_ij, axis=0, keepdims=True)/n

    B = -(dist_ij - dist_i -dist_j + dist)/2

    eig_value, eig_vector = np.linalg.eig(B)

    index_list = np.argsort(-eig_value)[:n_dims]

    picked_eig_value = eig_value[index_list].real
    picked_eig_vector = eig_vector[:, index_list]

    Z = picked_eig_vector*picked_eig_value**(0.5)
    return Z

if __name__=='__main__':
    iris = load_iris()
    data = iris.data
    Y = iris.target

    data_1 = Personal_MDS(data, 2)
    data_2 = MDS(n_components=2).fit_transform(data)

    plt.figure(figsize=(8, 4))
    plt.subplot(121)
    plt.title("Personal_MDS")
    plt.scatter(data_1[:, 0], data_1[:, 1], c=Y)

    plt.subplot(122)
    plt.title("sklearn_MDS")
    plt.scatter(data_2[:, 0], data_2[:, 1], c=Y)

    plt.show()


import numpy as np
import timeit
import random

class Item(object):
    def __init__(self, n, v, w):
        self.name = n
        self.value = int(v)
        self.weight = int(w)
    def getValue(self):
        return self.value
    def getWeight(self):
        return self.weight
    def getRatio(self):
        return self.getValue()/self.getWeight()
    def __str__(self):
        return f' Item: {self.name}, <Value: {str(self.value)}, Weight: {str(self.weight)}>'

def buildOptions( values, weights):
    items = []
    numItems = len(values)
    for i in range(numItems):
        items.append(Item(str(i), values[i],
                          weights[i]))
    return items

# A Greedy Approach: an approximate solution
def ksGreedy(items, knapsackCap, strategy):

    itemsCopy = sorted(items, key = strategy, reverse = True)
    taken = []
    totalVal, totalWeight = 0.0,0.0
    for i in range(len(itemsCopy)):
        if (totalWeight + itemsCopy[i].getWeight()) <= knapsackCap: #available resources
            taken.append(itemsCopy[i])
            totalVal += itemsCopy[i].getValue()
            totalWeight += itemsCopy[i].getWeight()
    return (totalVal, taken)

# Dynamic Programming approach bottom-up Iterative
def ksDP(items,knapsackCap):
    '''
    Input: the list of items(objects with weight and value)  and the capacity of the knapsack
    Returns: a tuple with total value and a solution to the problem
    '''
    # Build matrix in a bottom up-manner
    A = np.zeros([len(items),knapsackCap+1])
    for item in range(len(items)):
        for weight in range(knapsackCap+1):
            if item == 0 or weight == 0:
                A[item,weight] = 0
            elif weight >= items[item].getWeight(): # if available space
                A[item,weight] = max(A[item-1,weight],
                                     A[item-1,weight-items[item].getWeight()] +
                                     items[item].getValue())
            else: #no space available
                A[item, weight] = A[item-1, weight]
    #store results of knapsack
    total_value = A[-1][-1]
    cap = knapsackCap
    val = total_value
    taken = []
    for i in range(len(items), 0, -1):
        if val <= 0 : break
        elif val == A[i-1,cap]: continue
        else:
            val = val - items[i].getValue()
            cap = cap - items[i].getWeight()
            taken.append(items[i])
    return total_value,taken

#Print the solution
def printSolver(items, knapsackCap, algorithm, show = True):
    print('-------------------------------------')
    if algorithm == 'ksDP':
        print(f'Algorithm: DP')
        val,taken= ksDP(items, knapsackCap)
        if show :
            print(f'Value obtained  = {val}')
            for item in taken:
                print('   ', item)
    elif algorithm == 'ksGreedy-MaxVal':
        print(f'Algorithm: Greedy-MaxVal')
        strategy = Item.getValue #max Value
        val,taken= ksGreedy(items,knapsackCap,strategy)
        if show :
            print(f'Value obtained  = {val}')
            for item in taken:
                print('   ', item)
    elif algorithm == 'ksGreedy-MinWeight':
        print(f'Algorithm: Greedy-MinWeight')
        strategy = lambda x: 1/Item.getWeight(x) #min weight
        val,taken= ksGreedy(items,knapsackCap,strategy)
        if show :
            print(f'Value obtained  = {val}')
            for item in taken:
                print('   ', item)
    elif algorithm == 'ksGreedy-ValWeiRatio':
        print(f'Algorithm: Greedy-ValWeightRatio')
        strategy = Item.getRatio #ratio of value over size
        val,taken= ksGreedy(items,knapsackCap,strategy)
        if show :
            print(f'Value obtained  = {val}')
            for item in taken:
                print('   ', item)
    print('-------------------------------------')
    return int(val)

def buildInstance(num_items, max_V, max_W):
    '''
    Returns: examples of random items with weights and values
    '''
    random_items = []
    for item in range(num_items):
        random_items.append(Item(str(item),random.randint(1,max_V),
                          random.randint(1,max_W)))
    values = []
    weights = []
    for i in random_items:
        values.append(i.getValue())
        weights.append(i.getWeight())
    return random_items , values, weights

#load instances
def load_data(path):
    file = open(path, "r")
    data = file.readlines()
    values_set =[]
    weight_set = []
    for i,line in enumerate(data):
        items = line.split(',')
        if i == 0: numItems,knapsackCap = int(items[0]),int(items[1])  #Number of items, Knapsack capacity
        else:
            weight_set.append(int(items[0]))# For each bin: weight, profit
            values_set.append(float(items[1]))
    return knapsackCap,numItems, values_set, weight_set

if __name__ == '__main__':

    knapsackCap,numItems, values_set, weight_set = load_data("/Volumes/GoogleDrive/My Drive/MCCNotes/Jlab projects/GITHUB_repositories/DANY_repositories/Hyper-heuristic-Knapsack/Instances/test.txt")
    items = buildOptions(values_set,weight_set)

    #examples = [5,10,20,30,40,50,100,150,180,200]
    #examples = [60]
    #knapsackCap = 100
    #maxVal = 100
    #maxWei = 250
    #for j in examples:
        #numItems, numItems, weight_set  = buildInstance(j,maxVal,maxWei)

    print('-------------------------------------')
    print(f'Number of Items: {len(items)}')
    print(f'Knapsack Capacity: {knapsackCap}')
    print(f'Values List: {values_set}')
    print(f'Weights List: {weight_set}')
    val1 = printSolver(items, knapsackCap,'ksGreedy-MaxVal', True)
    time1 = timeit.timeit("ksGreedy(items,knapsackCap,Item.getValue)", number = 1 , globals = globals())
    print('Time: ', time1, 'seconds.')

    val2 = printSolver(items, knapsackCap,'ksGreedy-MinWeight', True)
    time2 = timeit.timeit("ksGreedy(items,knapsackCap,lambda x: 1/Item.getWeight(x))", number = 1 , globals = globals())
    print('Time: ', time2, 'seconds.')

    val3 = printSolver(items, knapsackCap,'ksGreedy-ValWeiRatio', True)
    time3 = timeit.timeit("ksGreedy(items,knapsackCap,Item.getRatio)", number = 1 , globals = globals())
    print('Time: ', time3, 'seconds.')

    val4 =printSolver(items, knapsackCap, 'ksDP', True)
    time4 = timeit.timeit('ksDP(items,knapsackCap)', number = 1 , globals = globals())
    print('Time: ',time4,'seconds.')

    methods = ['Greedy-MaxVal','Greedy-MinWeight','Greedy-ValWeiRatio','DP']
    vals = [val1, val2,val3,val4]
    times =[time1,time2,time3,time4]
    best_t = min(times)
    best_v = max(vals)
    ix_t = times.index(best_t)
    ix_v = vals.index(best_v)
    print('-------------------------------------')
    print('Best Solution')
    print('-------------------------------------')
    print(f'Best time: {best_t}, Method: {methods[ix_t]}.')
    print(f'Best value: {best_v}, Method: {methods[ix_v]}.')


import numpy as np

def dft(Xn):
    '''
    @Hypo 1910273011
    This is a function for Discrete Fourier Transform(DFT)

    Parameters:
    Xn : array_like, type = complex

    Returns:
    out : array_like, type = complex
    '''
    Xk = []; Xk_i = 0
    N = len(Xn)
    for k in range(N):
        for n in range(N):
            Xk_i += Xn[n]*np.exp(-1j*k*2*np.pi*n/N)
        Xk.append(Xk_i)
        Xk_i = 0
    return np.array(Xk)

def idft(Xk):
    '''
    @Hypo 1910273011
    This is a function for Inverse Discrete Fourier Transform(IDFT)

    Parameters:
    Xn : array_like, type = complex

    Returns:
    out : array_like, type = complex
    '''

    # use dft calculate idft
    return dft(Xk.conjugate()).conjugate()/len(Xk)

    #use formula
    '''
    Xn = []; Xn_i = 0
    N = len(Xk)
    for n in range(N):
        for k in range(N):
            Xn_i += Xk[k]*np.exp(1j*k*2*np.pi*n/N)
        Xn.append(Xn_i)
        Xn_i = 0
    return np.array(Xn)/N
    '''


from sklearn.metrics import roc_auc_score
import numpy as np

y_true = np.array([0, 0, 0, 0, 1, 1, 1, 1])
y_score = np.array([0.2, 0.3, 0.6, 0.8, 0.4, 0.5, 0.7, 0.9])

print(roc_auc_score(y_true, y_score))
# 0.6875

y_true_perfect = np.array([0, 0, 0, 0, 1, 1, 1, 1])
y_score_perfect = np.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8])

print(roc_auc_score(y_true_perfect, y_score_perfect))
# 1.0

np.random.seed(0)
y_true_random = np.array([0] * 5000 + [1] * 5000)
y_score_random = np.random.rand(10000)

print(roc_auc_score(y_true_random, y_score_random))
# 0.49895535999999996

y_score_inv = 1 - y_score
print(y_score_inv)
# [0.8 0.7 0.4 0.2 0.6 0.5 0.3 0.1]

print(roc_auc_score(y_true, y_score_inv))
# 0.3125

y_score_perfect_inv = 1 - y_score_perfect
print(y_score_perfect_inv)
# [0.9 0.8 0.7 0.6 0.5 0.4 0.3 0.2]

print(roc_auc_score(y_true_perfect, y_score_perfect_inv))
# 0.0


import numpy as np
import math

def DFT(A):
    n=len(A)
    if n==1:
        return A
    A0=[]
    A1=[]
    nunity_root=np.exp(complex(0,2*math.pi/n))
    omega=1
    for i in range(n):
        if i%2==0:
            A0.append(A[i])
        else:
            A1.append(A[i])
    A0=DFT(A0)
    A1=DFT(A1)
    Ar=[0 for _ in range(n)]
    for i in range(n/2):
        Ar[i]=A0[i]+(omega*A1[i])
        Ar[i+n/2]=A0[i]-(omega*A1[i])
        omega*=nunity_root
    return Ar

def IDFT(A):
    n=len(A)
    if n==1:
        return A
    nunity_root=np.exp(complex(0,(-2)*math.pi/2))
    omega=1
    A_0=[]
    A_1=[]
    for i in range(n):
        if i%2==0:
            A_0.append(A[i])
        else:
            A_1.append(A[i])
    A_0=IDFT(A_0)
    A1=IDFT(A_1)
    Ar=[0 for i in range(n)]
    for i in range(n/2):
        Ar[i]=A_0[i]+(omega*A_1[i])
        Ar[i+n/2]=A_0[i]-(omega*A_1[i])
        omega*=nunity_root
    Ar=[Ar[i]/n for i in range(n)]
    return Ar


import numpy as np

a = np.array([10, 20, 30, 40])
b = np.arange(4)

print("a=", a)
print("b=", b)
# 矩阵的加法
c = a + b
print("add c = ", c)
# b 的平方
print("square : ", b ** 2)
# 求矩阵的sin值,三角函数
print("sin : ", 10 * np.sin(a))
# 判断矩阵是否为3
print(b == 3)

# 矩阵的乘法运算
c = np.array([[1, 1], [0, 1]])
d = np.arange(4).reshape((2, 2))
# 1)单纯的矩阵中的值逐个相乘
print("矩阵的乘法运算:")
print(c * d)
# 2)矩阵的乘法运算
print(np.dot(c, d))
# print(c.dot(d))

# 随机填写0到1的数字 在一个2行4列的矩阵
rand_a = np.random.random((2, 4))
print("rand_a=", rand_a)

# 将整个矩阵求和
print("sum=", np.sum(rand_a))
# 求出矩阵中的最小值
print("min=", np.min(rand_a))
# 求出矩阵中的最大值
print("max=", np.max(rand_a))
# 求出矩阵中每一列的最小值
print("min2=", np.min(rand_a, axis=0))
# 求出矩阵中每一行的最小值
print("min3=", np.min(rand_a, axis=1))


import os
import getopt, sys
import numpy as np

# ---------- Partition ----------
def partition(arr, low, high):
  i = (low-1)
  pivot = arr[high]

  for j in range(low, high):
    if arr[j] <= pivot:
      i = i+1
      arr[i], arr[j] = arr[j], arr[i]

  arr[i+1], arr[high] = arr[high], arr[i+1]
  return (i+1)

# ---------- Quick Sort ----------
def quickSort(arr, low, high):
  if len(arr) == 1:
    return arr
  if low < high:
    pi = partition(arr, low, high)
    quickSort(arr, low, pi-1)
    quickSort(arr, pi+1, high)

# ---------- Merge ----------
def merge(arr, l, m, r):
  n1 = m - l + 1
  n2 = r - m

  L = [0] * (n1)
  R = [0] * (n2)

  for i in range(0 , n1):
    L[i] = arr[l + i]

  for j in range(0 , n2):
    R[j] = arr[m + 1 + j]

  i = 0
  j = 0
  k = l

  while i < n1 and j < n2 :
    if L[i] <= R[j]:
      arr[k] = L[i]
      i += 1
    else:
      arr[k] = R[j]
      j += 1
    k += 1

  while i < n1:
    arr[k] = L[i]
    i += 1
    k += 1

  while j < n2:
    arr[k] = R[j]
    j += 1
    k += 1

# ---------- Merge Sort ----------
def mergeSort(arr,l,r):
  if l < r:

    m = (l+(r-1))//2

    mergeSort(arr, l, m)
    mergeSort(arr, m+1, r)
    merge(arr, l, m, r)

# ---------- ShellSort ----------
def shellSort(arr):
  n = len(arr)
  interval = n // 2
  while interval > 0:
    for i in range(interval, n):
      temp = arr[i]
      j = i
      while j >= interval and arr[j - interval] > temp:
        arr[j] = arr[j - interval]
        j -= interval
      arr[j] = temp
    interval //= 2

# ---------- Sorting Algorithms ----------
def run_sorting_algorithm(algorithm, file_name, def_name, path):

  arr = np.random.randint(1000000, size=(100))
  np.savetxt('{}/{}-random.txt'.format(path, file_name), np.around(arr),fmt='%.0f',delimiter='\t')

  if file_name == 'quickSort' or file_name == 'mergeSort':
    k = len(arr)
    '{}'.format(def_name(arr, 0, k-1))
  else:
    '{}'.format(def_name(arr))
  np.savetxt('{}/{}-order.txt'.format(path, file_name), np.around(arr), fmt='%.0f',delimiter='\t')

# ---------- Main Code ----------
l = 100
path = str(l)
if not os.path.exists(path):
  os.mkdir(path)

full_cmd_arguments = sys.argv

argument_list = full_cmd_arguments[1:]

print (argument_list)

short_options = "qms"
long_options = ["quick_sort", "merge_sort", "shell_sort"]

try:
    arguments, values = getopt.getopt(argument_list, short_options, long_options)
except getopt.error as err:
    print (str(err))
    sys.exit(2)

for current_argument, current_value in arguments:
    if current_argument in ("-q", "--quick_sort"):
      print ("----- Quick Sort -----")
      run_sorting_algorithm('Quick Sort', 'quickSort', quickSort, path)
    elif current_argument in ("-m", "--merge_sort"):
      print ("----- Merge Sort -----")
      run_sorting_algorithm('Merge Sort', 'mergeSort', mergeSort, path)
    elif current_argument in ("-s", "--shell_sort"):
      print ("----- Shell Sort -----")
      run_sorting_algorithm('Shell Sort', 'shellSort', shellSort, path)