import numpy as np

def minor(A,i,j): #Calculo del determinante del menor
    m=[]
    rows=len(A)
    cols=rows
    for r in range(rows):
      l=[]
      for c in range(cols):
         if c != j:
            l.append(A[r][c])
      if r !=i:
        m.append(l)
    return np.linalg.det(m) #El menor de un elemento aij se denota Mij es el determinante de la matriz resultante al borrar el renglon i y la columna j de  A

def comatrix(A):  #Calculo de matriz de cofactores de A
    rows=len(A)
    cols=rows
    c=[]
    for i in range(rows):
        l=[]
        for j in range(cols):
          l.append((-1)**(i+j)*minor(A,i,j)) #Para calcular los cofactres de una matriz se necesita la menor de la cada elemento de la matriz A
                                             #la Formula es Aij = (-1)^(i+j)|Mij|
        c.append(l)
    return (c)

def adjunct(A): #Calculo de matriz adjunta, adjA= transpuesta de matriz de cofactores de A
    return np.transpose(comatrix(A))

def inverse(A): # Calculo de la Matriz inversa mediante la adjunta. A^-1 = [(A*)^T] / |A|
    '''
    Se pide que el usuario introduzca una matriz de nxn
	ya que para calacular la inversa esta debe ser una matriz cuadrada

	Consta de una serie de funciones para implementar la formula de la matrizinversa

			A^-1 = [(A*)^T] / |A|

		La matriz adjunta [(A*)^T] => tambien se conoce como la transpuesta de la matriz de cofactores
		Determinamte de A =>|A|

	Cada funcion se encarga de genrar una partep para la ecucion.
	Se calcula inicialmente la determinante de la matriz menor de A.
	Posteriormente se calcula la matriz de cofactores usando la siguiente fórmula

			Aij = (-1)^(i+j)|Mij|

	Una vez calculada la matríz de cofactores generamos la matriz adjunta
	simplmente se transpone la matriz de cofactores
	Finalmente, se halla 1/|A| y se multiplica por la matriz adjunta.

    '''
    return (1/np.linalg.det(A)*adjunct(A))

rows = int(input("ingrese el tamaño de la matriz: "))
matrix = []
print("ingrese la matriz %s x %s: "% (rows, rows))
for i in range(rows):
    matrix.append(list(map(int, input().rstrip().split())))

Aarry=np.array(matrix)

myAinv=inverse(Aarry)
print("my A inverse:\n",myAinv,"\n")

Ainv=np.linalg.inv(Aarry)
# Tambien se genera una matríz inversa utilizando unicamente la libreria de Numpy
print("numpy A inverse:\n",Ainv)


#Author
#Felice De Luca
#https://github.com/felicedeluca

##
# EU corresponds to the normalized standard deviation of the edge length.
##

import networkx as nx
import math

def avg_edge_length(G):

    '''
    Computes the average edge length of the given graph layout <tt>G</tt>
    '''

    sum_edge_length = 0.0

    pos_dict = nx.get_node_attributes(G, 'pos')

    for edge in nx.edges(G):

        (s,t) = edge

        x_source = float(pos_dict[s].split(",")[0])
        x_target = float(pos_dict[t].split(",")[0])

        y_source = float(pos_dict[s].split(",")[1])
        y_target = float(pos_dict[t].split(",")[1])

        curr_length = math.sqrt((x_source - x_target)**2 + (y_source - y_target)**2)

        sum_edge_length += curr_length

    edges_count = len(nx.edges(G))
    avg_edge_len = sum_edge_length/edges_count

    return avg_edge_len

def uniformity_edge_length(G):
    '''
    The Edge length uniformity corresponds to the normalized standard deviation of the edge length.
    '''

    edges = nx.edges(G)
    edge_count = len(edges)
    avgEdgeLength = avg_edge_length(G)
    tot_sum = 0.0

    pos_dict = nx.get_node_attributes(G, 'pos')

    for edge in edges:

        (s,t) = edge

        x_source = float(pos_dict[s].split(",")[0])
        x_target = float(pos_dict[t].split(",")[0])

        y_source = float(pos_dict[s].split(",")[1])
        y_target = float(pos_dict[t].split(",")[1])

        curr_length = math.sqrt((x_source - x_target)**2 + (y_source - y_target)**2)

        num = (curr_length-avgEdgeLength)**2
        den = edge_count*(avgEdgeLength**2)

        currValue = num/den
        tot_sum += currValue

    uniformity_e_len = math.sqrt(tot_sum)

    result = round(uniformity_e_len, 3)

    return result


# Simulate an inhomogeneous Poisson point process on a rectangle
# This is done by simulating a homogeneous Poisson process, which is then
# thinned according to a (spatially *dependent*) p-thinning.
# The intensity function is lambda(x,y)=exp(-(x^2+y^2)/s^2), where s>0.
# Author: H. Paul Keeler, 2019.
# Website: hpaulkeeler.com
# Repository: github.com/hpaulkeeler/posts
# For more details, see the post:
# hpaulkeeler.com/simulating-an-inhomogeneous-poisson-point-process/

import numpy as np;  # NumPy package for arrays, random number generation, etc
import matplotlib.pyplot as plt  # For plotting
from scipy.optimize import minimize  # For optimizing
from scipy import integrate  # For integrating

plt.close('all');  # close all figures

# Simulation window parameters
xMin = -1;
xMax = 1;
yMin = -1;
yMax = 1;
xDelta = xMax - xMin;
yDelta = yMax - yMin;  # rectangle dimensions
areaTotal = xDelta * yDelta;

numbSim = 10 ** 3;  # number of simulations

s = 0.5;  # scale parameter

# Point process parameters
def fun_lambda(x, y):
    return 100 * np.exp(-(x ** 2 + y ** 2) / s ** 2);  # intensity function
#fun_lambda = lambda x,y: 100 * np.exp(-(x ** 2 + y ** 2) / s ** 2);

###START -- find maximum lambda -- START ###
# For an intensity function lambda, given by function fun_lambda,
# finds the maximum of lambda in a rectangular region given by
# [xMin,xMax,yMin,yMax].
def fun_Neg(x):
    return -fun_lambda(x[0], x[1]);  # negative of lambda
#fun_Neg = lambda x: -fun_lambda(x[0], x[1]);  # negative of lambda

xy0 = [(xMin + xMax) / 2, (yMin + yMax) / 2];  # initial value(ie centre)
# Find largest lambda value
resultsOpt = minimize(fun_Neg, xy0, bounds=((xMin, xMax), (yMin, yMax)));
lambdaNegMin = resultsOpt.fun;  # retrieve minimum value found by minimize
lambdaMax = -lambdaNegMin;

###END -- find maximum lambda -- END ###

# define thinning probability function
def fun_p(x, y):
    return fun_lambda(x, y) / lambdaMax;
#fun_p = lambda x, y: fun_lambda(x, y) / lambdaMax;

# for collecting statistics -- set numbSim=1 for one simulation
numbPointsRetained = np.zeros(numbSim);  # vector to record number of points
for ii in range(numbSim):
    # Simulate a Poisson point process
    numbPoints = np.random.poisson(areaTotal * lambdaMax);  # Poisson number of points
    xx = np.random.uniform(0, xDelta, ((numbPoints, 1))) + xMin;  # x coordinates of Poisson points
    yy = np.random.uniform(0, yDelta, ((numbPoints, 1))) + yMin;  # y coordinates of Poisson points

    # calculate spatially-dependent thinning probabilities
    p = fun_p(xx, yy);

    # Generate Bernoulli variables (ie coin flips) for thinning
    booleRetained = np.random.uniform(0, 1, ((numbPoints, 1))) < p;  # points to be retained

    # x/y locations of retained points
    xxRetained = xx[booleRetained];
    yyRetained = yy[booleRetained];
    numbPointsRetained[ii] = xxRetained.size;

# Plotting
plt.scatter(xxRetained, yyRetained, edgecolor='b', facecolor='none', alpha=0.5);
plt.xlabel('x');
plt.ylabel('y');

# run empirical test on number of points generated
if numbSim >= 10:
    # total mean measure (average number of points)
    LambdaNumerical = integrate.dblquad(fun_lambda, xMin, xMax, lambda x: yMin, lambda y: yMax)[0];
    # Test: as numbSim increases, numbPointsMean converges to LambdaNumerical
    numbPointsMean = np.mean(numbPointsRetained);
    # Test: as numbSim increases, numbPointsVar converges to LambdaNumerical
    numbPointsVar = np.var(numbPointsRetained);


def five_num_summary(items):

    """The function takes a list as input and return a dict with keys 'max', 'median', 'min', 'q1', and 'q3'
    corresponding to the maximum, median, minimum, first quartile and third quartile, respectively.

    Example
    -------

           Input: gauteng = [39660.0,36024.0, 32127.0, 39488.0, 18422.0, 23532.0, 8842.0, 37416.0, 16156.0, 18730.0, 19261.0, 25275.0]

           Output: five_num_summ(gauteng) == {'max': 39660.0, 'median': 24403.5, 'min': 8842.0, 'q1': 18422.5, 'q3': 36024.5}

    """

    #Importing the module
    import numpy as np
    items = np.array(items)

    #Assigning the required data to 'num_summary'
    num_summary = {'max': round(np.max(items),2), 'median': round(np.median(items),2), 'min': round(np.min(items),2), 'q1': round(np.quantile(items,0.25),2), 'q3': round(np.quantile(items,0.75),2)}

    return num_summary


# A toy making company has at least
#
#	300 squares of felt,
#	700 oz of stuffing, and
#	230 ft of trim to make dogs and dinosaurs.

# A dog uses ( $1.36 )
#	1 square of felt,
#	4 oz of stuffing, and
#	1 ft of trim.
#
# A dinosaur uses ( $1.77 )
#	2 squares of felt,
#	3 oz of stuffing, and
#	1 ft of trim.

# It costs the company $1.25 to make each dog and $1.89 for each dinosaur.
# What is the company's minimum cost?

import numpy as np
from numpy import matrix, transpose, dot

dog_cost, dinosaur_cost = 1.36, 1.77

n_felt = 300
n_stuff = 700
n_trim = 230

felt = [ 1, 2 ]
stuff = [ 4, 3 ]
trim = [ 1, 1 ]

# Set up the constraints and obtain the corner points
a1 = np.array( [ felt, stuff ] )
b1 = np.array( [ n_felt, n_stuff ] )

# solve the two sets of equations at a time to get points of intersection of the lines
c1 = np.linalg.solve( a1, b1 )
print( c1 )

# Second pair of corner points
a2 = np.array( [ felt, trim ] )
b2 = np.array( [ n_felt, n_trim ] )
c2 = np.linalg.solve( a2, b2 )

print( c2 )

# Third pair of corner points
a3 = np.array( [ stuff, trim ] )
b3 = np.array( [ n_stuff, n_trim ] )
c3 = np.linalg.solve( a3, b3 )

print( c3 )

# Use the corner points and compute dot product to determine the minimum value
x= [ c1[ 0 ], c2[ 0 ], c3[ 0 ] ]
y= [ c1[ 1 ], c2[ 1 ], c3[ 1 ] ]

obj = matrix( [ dog_cost, dinosaur_cost ] )
obj = transpose( obj )
corners = matrix( [x,y] )
corners = transpose( corners )
result = dot( corners,obj )

print( "Value of Objective Function at Each Corner Point", result )
print( "Minimum cost of the company is ", result.min() )


import numpy as np
import numpy.linalg as la
import numpy.random as npr
import scipy.linalg as sla
from functools import reduce
import matplotlib.pyplot as plt

def mdot(*args):
    """Multiple dot product."""
    return reduce(np.dot, args)

def sympart(A):
    """Return the symmetric part of matrix A."""
    return 0.5*(A+A.T)

def gain(P, A, B, R):
    Hux = mdot(B.T, P, A)
    Huu = R+mdot(B.T, P, B)
    return -la.solve(Huu, Hux)

def lqr(A, B, Q, R, max_iters=None, threshold=1e-6):
    """
    Solve a infinite-horizon linear-quadratic regulation problem
    minimize sum(x[k].T, @ Q @ x[k] + u[k].T @ R @ u[k])
    subject to x[k+1] = A @ x[k] + B @ u[k]
    where
    A, B are dynamics matrices
    Q, R are positive semidefinite cost matrices
    """

    P = np.copy(Q)
    t = 0
    timeout = False
    converged = False
    while not converged or timeout:
        P_prev = np.copy(P)
        Hxx = Q+mdot(A.T, P, A)
        Hux = mdot(B.T, P, A)
        Huu = R+mdot(B.T, P, B)
        K = -la.solve(Huu, Hux)
        P = sympart(Hxx+np.dot(Hux.T, K))  # Take symmetric part to avoid numerical divergence issues
        converged = la.norm(P-P_prev, ord=2) < threshold
        if max_iters is not None:
            timeout = t > max_iters
        t -= 1
    return P, K

def rollout(x0, A, B, K):
    x_hist = np.zeros([T, n])
    u_hist = np.zeros([T, m])
    w_hist = np.zeros([T, n])
    x_hist[0] = np.copy(x0)
    for t in range(T-1):
        x = x_hist[t]
        u = K.dot(x)
        w = npr.multivariate_normal(wm, W)
        x_hist[t+1] = A.dot(x) + B.dot(u) + w
        u_hist[t] = u
        w_hist[t] = w
    return x_hist, u_hist, w_hist

def plot_hist(x_hist_all, u_hist_all, w_hist_all):
    fig, ax = plt.subplots(nrows=3)
    ylabels = ['State', 'Input', 'Disturbance']
    dims = [n, m, n]
    for i, (hist, ylabel, d) in enumerate(zip([x_hist_all, u_hist_all, w_hist_all], ylabels, dims)):
        for k in range(d):
            ax[i].plot(hist[:, :, k].T, color='C%d' % k, alpha=0.8)
        ax[i].set_ylabel(ylabel)
    ax[-1].set_xlabel('Time')
    fig.tight_layout()
    return fig, ax

if __name__ == "__main__":
    # Random seed
    npr.seed(4)

    # Problem data
    n = 5
    m = 2
    T = 30

    x0 = 5*npr.randn(n)

    A = npr.randn(n, n)
    # Normalize A to fix the spectral radius to r
    r = 2
    A = A*r/(max(abs(la.eig(A)[0])))
    B = npr.randn(n, m)

    Q = 1*np.eye(n)
    R = 1*np.eye(m)
    W = 0.1*np.eye(n)
    wm = np.zeros(n)

    # Solve DARE manually
    P, K = lqr(A, B, Q, R)

    # Solve DARE via SciPy
    Pscipy = sla.solve_discrete_are(A, B, Q, R)
    Kscipy = gain(Pscipy, A, B, R)

    # Compare DARE solutions
    print('Spectral norm of difference between P and Pscipy = %f' % la.norm(P-Pscipy, ord=2))
    print('Spectral norm of difference between K and Kscipy = %f' % la.norm(K-Kscipy, ord=2))

    # Simulate
    N = 10
    x_hist_all, u_hist_all, w_hist_all = np.zeros([N, T, n]), np.zeros([N, T, m]), np.zeros([N, T, n])
    for i in range(N):
        x_hist_all[i], u_hist_all[i], w_hist_all[i] = rollout(x0, A, B, K)

    # Plot
    plt.close('all')
    plt.style.use('../conlab.mplstyle')
    plot_hist(x_hist_all, u_hist_all, w_hist_all)


''' mbinary
#########################################################################
# File : least_square.py
# Author: mbinary
# Mail: zhuheqin1@gmail.com
# Blog: https://mbinary.xyz
# Github: https://github.com/mbinary
# Created Time: 2018-10-02  21:14
# Description:
#########################################################################
'''

'''*************************************************************************
    > File Name: least-square.py
    > Author: mbinary
    > Mail: zhuheqin1@gmail.com
    > Created Time: Sat 07 Apr 2018 09:55:25 PM DST
    > Blog: https://mbinary.xyz
    > Description: 最小二乘法解线性方程组, 解矛盾方程组
 ************************************************************************'''

import re
import numpy as np

def solveConflitEqualtion(A,y):
    '''solve equation like this: Av = y,
        A:m*n  v:n*1  y:m*1
        return vector v
    '''
    A = np.matrix(A)
    y = np.array(y)
    ata = A.T*A
    print('AtA')
    print(ata)
    return np.linalg.solve(ata,A.T*y) # note that is numpy.linalg.solve

def solveLinear(point,index):
    y = [[i[1]] for i in point]
    x = [[i[0]] for i in point]
    A = []
    for i in x:
        A.append([i[0]**j for j in index])
    res =  solveConflitEqualtion(A,y)
    print('the solution is : \n',res)
    print('namely: ')
    items = ['{:.4f}x^{}'.format(res[i,0],j)   for i, j in enumerate(index)]
    print('phi(x) = ',' + '.join(items))

def handleInput(s=None,y=None):
    # numPt = re.compile (r'\d*\.{0,1}\d+')
    if not s: s = input('input matrix A:m*n  //m>=n\n')
    s = s.replace(' ','')
    li = re.findall(r'(\[(\d+)(,(\d+))+\])',s)
    li = [parseLst(i[0]) for i in li]
    if not y:y = input('input a vector y:n*1\n')
    y = parseLst(y)
    print('Equation: Av = y:')

    print('y is as follows: ')
    print(y)
    print('A is as follows: ')
    for i in li:
        for j in i:
            print('{}'.format(j).rjust(5),end='')
        print('')

    print('result v is as follows: ')
    res = solveConflitEqualtion(li,y)
    print(res)
def parseLst(s):
    s = s.strip('[]')
    li = s.split(',')
    li = [float(j) for j in li]
    return li

if __name__ == '__main__':
    '''
    li = '[[23,2],[2,5],[2,6]]'
    y = '[1,3]'
    while True:
        handleInput(li,y)
        s = input('input y to continue, n for exit')
        if s!='y':break
    '''
    point = [(-3,14.3),(-2,8.3),(-1,4.7),(2,-8.3),(4,-22.7)]
    lst = [0,3]
    solveLinear(point,lst)

    point= [(-3,14.3),(-2,8.3),(-1,4.7),(2,8.3),(4,22.7)]
    lst = [0,2]
    solveLinear(point,lst)

    A = [[1,2],[2,1],[1,1]]
    y  = [[5],[6],[4]]
    res = solveConflitEqualtion(A,y)

    print(res)
    A = [[1,-2],[1,5],[2,1],[1,1]]
    y = [[1],[13.1],[7.9],[5.1]]
    print(solveConflitEqualtion(A,y))


# coding: utf-8
#
# Copyright (C) 2020 wsn-toolkit
#
# This program was written by Edielson P. Frigieri <edielsonpf@gmail.com>

"""Path loss models."""

from abc import ABCMeta, abstractmethod
import numpy as np
import math

DEFAULT_C = 2.998e8
MIN_LOSS = 0

class BasePropagationModel(metaclass=ABCMeta):
	"""Base class for propagation loss models."""

	def __init__(self):
		pass

	def _friis_loss(self, distance, frequency):
		"""
		Calculate the path loss based on Friss propagation loss model.

		Formula:
				PL0 = 10*log10(Gr*Gt*((4*pi*d*f)/c)^2)

		Parameters
		----------
		distance : double
		   The distance (m).

		frequency: double
			The frequency of operation (Hz).

		Returns
		-------
		double
			The Friss loss in decibels [dBm] evaluated for `distance` and `frequency`.
		"""
		Gr=1
		Gt=1

		return 10*math.log10(Gr*Gt*((4*math.pi*distance*frequency)/DEFAULT_C)**2)

	@abstractmethod
	def loss(self, distance, frequency):
		"""Calculate the path loss."""
		raise NotImplementedError

class FreeSpace(BasePropagationModel):
	"""Class for Log-nomal propagation models."""

	def __init__(self):
		super(FreeSpace).__init__()

	def loss(self, distance, frequency):
		"""
		Calculate the link loss.
		For distance and frequency in meters and hertz, respectively.

		Calculates the loss based on the free-space Friis equation

		Parameters
		----------
		distance : double
		   The distance between two nodes (m).

		frequency: double
			The frequency of operation (Hz).

		Returns
		-------
		double
			The loss evaluated for `distance` and `frequency`.
		"""
		if distance > 0.1:
			L = self._friis_loss(distance, frequency)
		else:
			L = MIN_LOSS

		return L

class LogDistance(BasePropagationModel):
	"""Class for Log-nomal propagation models."""

	def __init__(self, d0 = 1.0, sigma = 0.0, n0 = 2.0):
		self.d0 = d0
		self.sigma = sigma
		self.n0 = n0
		super(LogDistance).__init__()

	def loss(self, distance, frequency):
		"""
		Calculate the link loss.
		For distance and frequency in meters and hertz, respectively.

		The log-normal path-loss model may be considered as a generalization of the free-space Friis equation
		where a random variable is added in order to account for shadowing (large–scale fading) effects.

		See:    https://www.sciencedirect.com/topics/computer-science/path-loss-model
				https://en.wikipedia.org/wiki/Log-distance_path_loss_model

				Building Type	            Frequency of Transmission	n0 	sigma [dB]
				Vacuum, infinite space		                            2.0	    0
				Retail store	            914 MHz	                    2.2	    8.7
				Grocery store	            914 MHz	                    1.8	    5.2
				Office with hard partition	1.5 GHz	                    3.0	    7
				Office with soft partition	900 MHz	                    2.4	    9.6
				Office with soft partition	1.9 GHz	                    2.6	    14.1
				Textile or chemical	        1.3 GHz	                    2.0	    3.0
				Textile or chemical	        4 GHz	                    2.1	    7.0, 9.7
				Office	                    60 GHz	                    2.2	    3.92
				Commercial	                60 GHz	                    1.7	    7.9

		Formula:
				PL = PL0 + 10*n0*log10(d/d0) + X_sigma

		Parameters
		----------
		distance : double
		   The distance between two nodes (m).

		frequency: double
			The frequency of operation (Hz).

		Returns
		-------
		double
			The loss evaluated for `distance` and `frequency`.
		"""
		if distance < self.d0:
			L = self._friis_loss(distance, frequency) + np.random.normal(0, self.sigma)
		else:
			L = self._friis_loss(self.d0, frequency) + self.n0*10*math.log10(distance/self.d0) + np.random.normal(0, self.sigma)
		return L

class TwoSlope(BasePropagationModel):
	"""Class for Log-nomal propagation models."""

	def __init__(self, d0 = 1.0, d1 = 10.0, sigma = 0.0, n0 = 2.0, n1 = 3.0):
		self.d0 = d0
		self.d1 = d1
		self.sigma = sigma
		self.n0 = n0
		self.n1 = n1
		super(TwoSlope).__init__()

	def loss(self, distance, frequency):
		"""
		Calculate the link loss.
		For distance and frequency in meters and hertz, respectively.

		The two-slope path-loss model implements a log distance path loss propagation model with two distance fields.
		This model is the same as the LogDistance model except that it has two distance fields: near, far with different exponents.

		Within each field the reception power is calculated using the log-distance propagation equation:

			PL = PL0 + 10*n0*log10(d/d0)

		where each field begins where the previous ends and all together form a continuous function.

		A random variable is added in order to account for shadowing (large–scale fading) effects.

		Formula:

				PL = PL0  + X_sigma 															, for d < d0
				PL = PL0 + 10*n0*log10(d/d0) + X_sigma 											, for d0<= d < d1
				PL = PL0 + 10*n0*log10(d1/d0) + 10*n1*log10(d/d1)  + X_sigma					, for d1 <= d

		Parameters
		----------
		distance : double
		   The distance between two nodes (m).

		frequency: double
			The frequency of operation (Hz).

		Returns
		-------
		double
			The loss evaluated for `distance` and `frequency`.
		"""
		if distance < self.d0:
			L = self._friis_loss(distance, frequency) + np.random.normal(0, self.sigma)
		elif self.d0 <= distance < self.d1:
			L = self._friis_loss(self.d0, frequency) + self.n0*10*math.log10(distance/self.d0) + np.random.normal(0, self.sigma)
		else:
			L = self._friis_loss(self.d0, frequency) + self.n0*10*math.log10(self.d1/self.d0) + self.n1*10*math.log10(distance/self.d1) + np.random.normal(0, self.sigma)

		return L


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

# Task 1
# ======

# given data
m1_m=24.2   # population 1 mean
m1_s=6.1    # population 1 standard deviation
m2_m=25.9   # population 2 mean
m2_s=5.5    # population 2 standard deviation
t2m=24.5
n=30

# calculations
m1_error=m1_s/(float(n))**0.5   # standard error if we sampled from pop 1
m2_error=m2_s/(float(n))**0.5   # standard error if we sampled from pop 2
m1_z=(t2m-m1_m)/m1_error        # standard score if sampled from pop 1
m2_z=(t2m-m2_m)/m2_error        # standard score if sampled from pop 2

print ("P(T>24.5 | M1) :",1-(norm.cdf(m1_z, 0, 1)))
print ("P(T>24.5 | M2) :",1-(norm.cdf(m2_z, 0, 1)))

# plotting
x1 = np.linspace(m1_m - 3*m1_s, m1_m + 3*m1_s, 100)   # create data from population parameters
x2 = np.linspace(m2_m - 3*m2_s, m2_m + 3*m2_s, 100)   # create data from population parameters
plt.plot(x1, norm.pdf(x1, m1_m, m1_s))                # create plot
plt.plot(x2, norm.pdf(x2, m2_m, m2_s))                # create plot

x1 = np.linspace(m1_m - 3*m1_error, m1_m + 3*m1_error, 100)   # create data from distribution
x2 = np.linspace(m2_m - 3*m2_error, m2_m + 3*m2_error, 100)   # create data from distribution
plt.plot(x1, norm.pdf(x1, m1_m, m1_error))                # create plot
plt.plot(x2, norm.pdf(x2, m2_m, m2_error))                # create plot

plt.show()

# Task 2
# ======

s=1.1 # estimate for population standard deviation
# Find distance in standard deviations for a confidence interval of 95. Note that this function in python takes the left integral. To get the distance from the mean, integrate to 1-0.25 (because you leave out the 0.25 tails on each side). The following give you the upper and lower bound.
z1=norm.ppf(.975)    # percent point function
z2=norm.ppf(.025)    # percent point function
# Since the distribution is symmetrical, we can only use one of the z's from now on. The question wants to know the sample number required to make sure that the 95% confidence interval is the true mean +/- 0.5. We can therefore say abs(z2) = z1 = 0.5. We have an estimate for the population standard deviation and can estimate our sampling distribution standard deviation from by: 0.5/z1. Therefore, we can calculate the samples required for this interval as:
ns=(1.1/(0.5/z1))**2
print ('min. no. of samples required: ', ns)


import numpy as np

def premier(n):   # entrée : entier n / sortie : booléen (True si premier)
                  # teste si un nombre n est premier

    if n == 1 or n == 2:    # 1 et 2 sont premiers

        return True

    if n%2 == 0:     # si n est un nombre pair

        return False

    r = n**0.5     # racine carrée de n

    if r == int(r):     # si la racine carrée de n est un nombre entier, n n'est pas premier

        return False

    for x in range(3, int(r), 2):     # intervalle [3 ; n-1] avec un pas de 2 (nombres impairs)

        if n % x == 0:     # si n est divisible par x, il n'est pas premier

            return False

    return True      # true si n est premier

def cut(k, long):     # entrée : k = chaîne de caractères / sortie : long = liste de blocs de chaînes de caractères
                      # découpe k en blocs de longueur long

    d = 0
    f = long

    l = []

    while f <= len(k):      # tant que la longueur à découper est inférieure à la longueur de la chaîne de caractères

        l.append(k[d:f])     # découpe un bloc de longeuur f dans la chaîne de caractères à partir de d (initialisé à 0) jusqu'à f

        d = f
        f = f + long    # on passe au bloc suivant

    m = len(k)%long

    if m != 0:     # si k n'est pas vide (nombre de caractères restant < f)

        l.append(k[len(k)-m:])     # ajoute les caractères restant à l

    return l     # renvoie k transformée en liste de blocs de chaînes de caractères de longueur l

def pgcde(a, b):  # entrée : entiers a et b / sortie : r PGCD de a et b, u et v coefficients de bézout tels que au + bv = r

    r = a
    u = 1
    v = 0
    rp = b
    up = 0
    vp = 1

    while rp != 0:   # algorithme d'Euclide étendu (pour trouver les coefficients)
        q = r//rp
        rs, us, vs = r, u, v
        r, u, v = rp, up, vp
        rp, up, vp = (rs - q*rp), (us - q*up), (vs - q*vp)
        assert u*a + v*b == r

    return (r, u, v)

def key():    # sortie : tuple de deux entiers
              # génère une paire de clés (publique / privée) sous forme de tuple

    p = np.random.choice(1000,1)    # nombre choisi aléatoirement entre 0 et 999 avec un pas de 1
    q = np.random.choice(1000,1)    # nombre choisi aléatoirement entre 0 et 999 avec un pas de 1

    while not premier(p):
        p = np.random.choice(1000,1)  # tant que p n'est pas premier, choisit un nouveau nombre aléatoire entre 0 et 999 avec pas de 1

    while not premier(q):
        q = np.random.choice(1000,1)    # tant que p n'est pas premier, choisit un nouveau nombre aléatoire entre 0 et 999 avec pas de 1

    n = p*q   # multiplication des deux nombres premiers (module de chiffrement)
    m = (p-1)*(q-1)   # valeur de l'indicatrice d'Euler en n

    r = 10
    d = 0
    while r != 1 or d <= 2 or d >= m:
        c = np.random.choice(1000,1)    # exposant de chiffrement premier avec m et strictement inférieur à n
        r, d, v = pgcde(c,m)      # bézout car c et m premiers entre eux

    n, c, d = int(n), int(c), int(d)      # nombres entiers

    return {"priv":(n,c), "pub":(n,d)}  # clé privée et clé publique

def chiffre(n, c, msg):     # entrée : clé privée c (int) et message à chiffrer (str) / sortie : liste de chaînes de caractères

    asc = [str(ord(j)) for j in msg]      # conversion du message en codes ascii

    for i, k in enumerate(asc):     # ajout de 0 pour que chaque code ascii soit de longueur 4
        if len(k) < 3:

            while len(k) < 3:

                k = '0' + k

            asc[i] = k

    ascg = ''.join(asc)     # chaîne de caractères qui contient tous les codes ascii concaténés

    f = 4   # découper ascg en blocs de longueur 4

    while len(ascg)%f != 0:   # on rajoute des 0 tant que len(ascg) n'est pas divisible par 4

        ascg = ascg + '0'

    l = cut(ascg, f)    #liste composée d'ascg découpée en blocs de longueur f

    crypt = [str((int(i)**c)%n) for i in l]  # exponentiation modulaire : message à la puissance c modulo n

    return crypt

def dechiffre(n, d, *crypt):    #  entrée : clé publique (int) et message à déchiffrer (liste) / sortie : chaîne de caractères correspondant au message déchiffré

    resultat = [str((int(i)**d)%n) for i in crypt]   # exponentiation modulaire

    for i, s in enumerate(resultat):   # ajout de 0 pour que chaque code ascii soit de longueur 5

        if len(s) < 4:

            while len(s) < 4:

                s = '0' + s

            resultat[i] = s

    g = ''.join(resultat)   # chaîne de caractères qui contient tous les codes ascii concaténés

    asci = ''

    d , f = 0 , 3

    while f < len(g):

        asci = asci + chr(int(g[d:f]))   # convertit en caractère correspondant au code de caractère Unicode (de d à f)

        d , f = f , f + 3

    return asci


from sympy import *
import matplotlib.pyplot as plt
import numpy as np

# Suppose that the cost function for a product is given by
x = symbols('x', positive = True ) # 0 <= x
C = 0.002 * x**3 + 7*x + 7626

avgC = C / x

g_xlim = [ 1, 500 ]

lam_c = lambdify( x, avgC, np )

x_vals = np.linspace( g_xlim[0], g_xlim[1], 10000, endpoint=True )
y_vals = lam_c( x_vals )

plt.plot( x_vals, y_vals, label = '' )

daC = diff( avgC, x, 1 )

# The production level that produces the minimum average cost per unit is
min = solve( daC, x )[ 0 ]
min

# (Round to the nearest whole number as needed.)
round( min )

plt.show()


"""
REGRESSÃO LINEAR SIMPLES => Existe apenas 1 atributo previsor
REGRESSÃO LINEAR MÚLTIPLA -> Mais de 1 atributo previsor

Variável x são as variáveis independentes (explantatórias)
Variável y é a variável independente (a qual buscamos prever)

A correlação vai de -1 a 1, onde valores -1 e 1 são correlações perfeitas
que significa que existe total correação. Se o for 1 significa que uma variável
aumenta e a outra também, correlação positiva.
Se o valor for -1 significa que a correlação é negativa, onde uma variável
aumenta e a outra diminui.
O valor 0 para correlação significa que não há correlação.

# NOTA:
É muito importante a análise da correlação, pois se tivermos valores muito
baixos (próximos de 0) provavel que seu modelo de regressão linear não vai se
adaptar muito bem a sua base de dados.

Neste exemplo como temos uma correlação de 0.93, é uma correlação forte,
portanto, é um primeiro indicativo que o modelo de regressão linear pode ser
usado.
"""

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.linear_model import LinearRegression
from yellowbrick.regressor import ResidualsPlot

base = pd.read_csv("plano-saude.csv")

X = base.iloc[:, 0].values
y = base.iloc[:, 1].values

# Iremos verificar se existe algum tipo de correlação entre as variáveis x e y
# A palavra correlação vem da estatística e indica quanto uma variável está
# próxima de outra, ou seja, se uma variável está ou não correlacionada.
correlacao = np.corrcoef(X, y)

# Veja o resultado da correlação é uma matriz 2 por 2 onde, a intersecção do 0 com 0
# o resultado é 1, ou seja, a correlação da variável idade com ela mesma é 1, e
# a correlação da variável 0 (idade) com a 1 (custo) é de 0.9390 (93%)
# Quanto maior o valor, melhor, ou seja, maior correlação, por isso na diagonal
# principal temos os valores de 1, que são as correlações das variáveis com
# elas mesmas, que é totalmente correlacionada (100%)

# Iremos alterar a forma (shape) das variáveis para matriz, pois os algoritmos
# do scikit-learn precisam que os parâmetros sejam matrizes
# Usamos o -1 para não mexer nas linhas e o 1 para adicionar uma coluna
X = X.reshape(-1, 1)

# Criação do regressor
regressor = LinearRegression()

# treinamento do regressor
# O treinamento do algoritmo de regressão linear é para encontrar os melhores
# parâmetros do b0 e b1 que são a constante e o coeficiente, respectivamente
regressor.fit(X, y)

# Exibe o valor de b0 (constante)
print(regressor.intercept_)

# Exibe o valor de b1 (coeficiente)
# Temos um coeficiente para cada uma das variáveis previsores
# Como temos apenas 1 variável previsora neste exemplo, será apenas 1 valor
print(regressor.coef_)

# Utiliza a lib matplot para exibir um gráfico com os valores (Reais) de X e y
plt.scatter(X, y)

# Criando linha da regressão linear após encontrar o b0 e b1 (Valores previstos)
# Será adicionado a linha da regressão linear de acordo com a previsão
plt.plot(X, regressor.predict(X), color='red')

# Adicionando título ao gráfico
plt.title("Regressão Linear Simples")
# adicionando nome para os eixos
plt.xlabel("Idade")
plt.ylabel("Custo")

# Realizando a previsão de um novo registro
# previsão pessoa com 40 anos
previsao1 = regressor.predict(np.array(40).reshape(1, -1))
# Podemos reescrever a previsão acima utilizando os parâmetros b0 + b1 * valor X
previsao2 = regressor.intercept_ + regressor.coef_ * 40

# O valor previsto para o custo do plano de saúde foi de 1.915 reais

# Verificando como a correlação está se comportando
# O valor retornado é diferente do valor de coeficiente de correlação encontrado
# anteriormente, o valor do score é o valor de correlação do seu modelo de regressão
# Quanto valores maiores melhor
# Mede quão bom é o seu modelo
score = regressor.score(X, y)

# Valores de residuais são as distâncias que existem entre os pontos (reais) e a linha reta (prevista)
# Passamos como parâmetro o regressor que já foi treinado
visualizador = ResidualsPlot(regressor)
visualizador.fit(X, y)
visualizador.poof()


# LINEAR REGRESSION
#
# y = b1 * x + b0
#
# To find b0 and b1 there are 2 ways:
#
# 1. Karl's correlation coeff
# 2. Least square method
#
# Steps (using Karl's Correlation Coeff Method):
# 1. Find summation of X (x_sum)
# 2. Find summation of Y (y_sum)
# 3. Find x * y and its summation
# 4. Find (x) ** 2 and its summation
# 5. Find b1 using formula
# 6. Find b0 using formula
# 7. Find y using regression formula

import numpy as np
import pandas as pd
from sklearn.linear_model import LinearRegression

kcoeff = []

df = pd.DataFrame({
    "X" : [0, 1, 2, 3, 4],
    "Y" : [2, 3, 5, 4, 6]
})
def calculateXY():
    df["(X) * (Y)"] = df["X"] * df["Y"]

def calculateXSquare():
    df["(X) ^ 2"] = df["X"] ** 2

def calculateKcoeff(x_val):
    xy_sum = df["(X) * (Y)"].sum()
    x_sum = df["X"].sum()
    y_sum = df["Y"].sum()
    xsquare_sum = df["(X) ^ 2"].sum()
    n = df["X"].count()

    X_mean = df["X"].mean()
    Y_mean = df["Y"].mean()

    b1 = round((n*xy_sum - (x_sum*y_sum)) / (n*xsquare_sum-pow(x_sum,2)), 2)
    b0 = round(Y_mean - b1 * X_mean, 2)
    kcoeff.append(b0)
    kcoeff.append(b1)
    print(kcoeff)
    print(x_val)
    y = round(kcoeff[0] + kcoeff[1] * x_val, 2)
    return y

def scikitlearn_linear_reg(x_val):
    X = df["X"].values
    X = np.reshape(X, (-1, 1))
    Y = df["Y"].values
    Y = np.reshape(Y, (-1, 1))
    reg = LinearRegression().fit(X, Y)
    y = round(reg.predict(np.array([[x_val]])).flatten()[0], 2)
    return y

if __name__ == "__main__":
    x = int(input("Enter x value: "))
    calculateXY()
    calculateXSquare()
    y_custom = calculateKcoeff(x)
    y_scikit = scikitlearn_linear_reg(x)
    print("FINAL DATA:")
    print(df.head(6))
    print(f"For x = {x}")
    print(f"By custom linear regression model: y = {y_custom}")
    print(f"By scikit-learn linear regression model: y = {y_scikit}")

########### OUTPUT ###########

# Enter x value: 10
# [b0, b1] = [2.2, 0.9]
# x = 10
# FINAL DATA:
#    X  Y  (X) * (Y)  (X) ^ 2
# 0  0  2          0        0
# 1  1  3          3        1
# 2  2  5         10        4
# 3  3  4         12        9
# 4  4  6         24       16
# For x = 10
# By custom linear regression model: y = 11.2
# By scikit-learn linear regression model: y = 11.2


import numpy as np

# loss functions and derivative
# mean squared error ou encore moyenne de l'erreur au carré
def mse(y_true, y_pred):
    return np.mean(np.power(y_true-y_pred, 2))

def mse_prime(y_true, y_pred):
    return 2 * (y_pred - y_true) / len(y_true)

# getting loss when using logistic regression ( softmax function )

def get_loss(y_true, y_pred):
  return -1 * np.sum(y_true * np.log(y_pred))

def get_loss_numerically_stable(y_true, y_pred):
   return -1 * np.sum(y_true * (y_pred + (-y_pred.max() - np.log(np.sum(np.exp(y_pred-y_pred.max()))))))


import numpy as np

class PiecewiseQuadratic(object):
    """Interpolate by piecewise quadratic polynomials data from a univariate function.

    This class represents a piecewise quadratic polynomial that passes through a series
    of (x,y) data points. The resulting continous function will have a continous derivative
    with prescribed value of first derivative at the inital data point (x_0,y_0).

    Args:
        times (:obj:`numpy array`): Time points at which the data was collected, `shape=(N,)`.
        data (:obj:`numpy array`): The scalar data values collected at the timepoints, `shape=(N,)`.
        bc_indx (:obj:`float`): Index of data point at which to enforce derivative value ```bc_deriv```. Defaults to zero.
        bc_deriv (:obj:`float`): Value of first derivative to enforce at times[```bc_indx```]. Defaults to zero.

    """

    def __init__(self, times, data, bc_indx=0, bc_deriv=0):
        assert np.allclose( np.sort(times), times ), "Input times needs to be sorted"
        self._times = times
        self._data  = data
        self._Mi = np.array([ [-1,  1,  -1],
                             [ 0,  0,   1],
                             [ 1,  0,   0]] )
        self._point_derivatives = self._get_point_derivatives( times, data, bc_indx, bc_deriv )

    def _get_point_derivatives(self, times, data, bc_indx, bc_deriv):
        """Find the first derivatives at the given timepoints to speed up later interpolation evaluation.

        The idea is to solve for the values as a chain starting with setting the first derivative to `bc_start`,
        fitting the first quadratic. The derivative of this interval at the endpoint is then passed along
        to fit the next quadratic and so on. Only the derivatives are saved rather than the polynomial
        coefficents to save memory. The resulting point derivatives are expressed in the local basis, i.e
        each polynomial exist on a [0,1] intervall mapping.

        Args:
            times (:obj:`numpy array`): Time points at which the data was collected, `shape=(N,)`.
            data (:obj:`numpy array`): The scalar data values collected at the timepoints, `shape=(N,)`.

        Returns:
            point_derivatives (:obj:`numpy array`): Derivatives of the piecewise polynomial at input times, `shape=(N,)`.

        """

        point_derivatives = np.zeros((len(times),))

        point_derivatives[bc_indx] = bc_deriv * (times[bc_indx+1] - times[bc_indx]) # local basis scale
        # NOTE: The point derivatives are in the local basis scale in a forward sense. i.e point_derivatives[i]
        # is in the scale of region i, which has lenght dt = times[i+1] - times[i].

        base = 2*( data[1:] - data[:-1] )
        for region in range(bc_indx, len(times)-2, 1): # forward marching +
            # Here we may first solve, and then convert to the local scale of derivative
            point_derivatives[region+1] = (base[region] - point_derivatives[region]) * ( (times[region+2] - times[region+1]) / (times[region+1] - times[region]) )

        for region in range(bc_indx, 0, -1 ): # backwards marching -
            # Here we must first convert to local derivative scale and then solve.
            point_derivatives[region-1] = base[region-1] - point_derivatives[region] * ( (times[region] - times[region-1]) / (times[region+1] - times[region]) )

        return point_derivatives

    def _time_to_intervall(self, time):
        """Find integer index of polynomial interval containing input time.
        """
        indx = np.argsort(np.abs(self._times-time))[0]
        if self._times[0] < time <= self._times[indx]:
            indx = indx - 1
        return indx

    def __call__(self, t, derivative=0):
        """Evaluate the piecewise polynomial or any of its derivatives at input points.

        Args:
            t (:obj:`iterable`): Time points at which to evaluate the piecewise polynomial.
            derivative (:obj:`int`): Order of desired derivative to evaluate. Default is 0.

        Returns:
            (:obj:`numpy array`) the polynomial values at input times.

        """
        out = np.zeros((len(t),))
        for i in range(len(t)):

            indx = self._time_to_intervall(t[i])
            c = self._Mi.dot( np.array([self._data[indx], self._data[indx+1], self._point_derivatives[indx]]) )
            xpar = (t[i] - self._times[indx]) / (self._times[indx+1] - self._times[indx])

            assert xpar>=0.0 and xpar<=1.0, "xpar="+str(xpar)+" _times[indx+2]="+str(self._times[indx+2])+" _times[indx+1]="+str(self._times[indx+1])+" _times[indx]="+str(self._times[indx])+" t[i]="+str(t[i])

            if derivative==0:
                out[i] = c[0]*xpar**2 + c[1]*xpar + c[2]
            elif derivative==1:
                out[i] = (2*c[0]*xpar + c[1]) / (self._times[indx+1] - self._times[indx])
            elif derivative==2:
                out[i] = 2*c[0] / (self._times[indx+1] - self._times[indx])**2
            else:
                out[i] = 0

        return out


import numpy as np

def sigmoid(t):
    """
    Applies the sigmoid function elementwise to the input data.

    Parameters
    ----------
    t : array, arbitrary shape
        Input data.

    Returns
    -------
    t_sigmoid : array, arbitrary shape.
        Data after applying the sigmoid function.
    """
    return 1/(1+np.exp(-t))

def negative_log_likelihood(X, y, w):
    """
    Negative Log Likelihood of the Logistic Regression.

    Parameters
    ----------
    X : array, shape [N, D]
        (Augmented) feature matrix.
    y : array, shape [N]
        Classification targets.
    w : array, shape [D]
        Regression coefficients (w[0] is the bias term).

    Returns
    -------
    nll : float
        The negative log likelihood.
    """
    return np.sum(-y*(X@w)+np.log(1+np.exp(X@w)))

def compute_loss(X, y, w, lmbda):
    """
    Negative Log Likelihood of the Logistic Regression.

    Parameters
    ----------
    X : array, shape [N, D]
        (Augmented) feature matrix.
    y : array, shape [N]
        Classification targets.
    w : array, shape [D]
        Regression coefficients (w[0] is the bias term).
    lmbda : float
        L2 regularization strength.

    Returns
    -------
    loss : float
        Loss of the regularized logistic regression model.
    """
    # The bias term w[0] is not regularized by convention
    return negative_log_likelihood(
        X, y, w) / len(y) + lmbda * 0.5 * np.linalg.norm(w[1:]) ** 2

def get_gradient(X, y, w, mini_batch_indices, lmbda):
    """
    Calculates the gradient (full or mini-batch) of the negative log likelilhood w.r.t. w.

    Parameters
    ----------
    X : array, shape [N, D]
        (Augmented) feature matrix.
    y : array, shape [N]
        Classification targets.
    w : array, shape [D]
        Regression coefficients (w[0] is the bias term).
    mini_batch_indices: array, shape [mini_batch_size]
        The indices of the data points to be included in the (stochastic) calculation of the gradient.
        This includes the full batch gradient as well, if mini_batch_indices = np.arange(n_train).
    lmbda: float
        Regularization strentgh. lmbda = 0 means having no regularization.

    Returns
    -------
    dw : array, shape [D]
        Gradient w.r.t. w.
    """
    sample_size = y.shape[0]
    batch_size = mini_batch_indices.shape[0]
    X = X[mini_batch_indices]
    y = y[mini_batch_indices]
    return -sample_size / batch_size * (X.T @ (y - 1 / (1 + np.exp(-X @ w))))

def predict(X, w):
    """
    Parameters
    ----------
    X : array, shape [N_test, D]
        (Augmented) feature matrix.
    w : array, shape [D]
        Regression coefficients (w[0] is the bias term).

    Returns
    -------
    y_pred : array, shape [N_test]
        A binary array of predictions.
    """
    y_pred = sigmoid(X @ w)
    y_pred[y_pred <= 0.5] = 0
    y_pred[y_pred > 0.5] = 1
    return y_pred


import numpy as np

def decimal_to_fixed_p (num, width, int_bit, frac_bit):
    '''
    given number, width, integer bit and fractinal bit,
    returns the fixed point representation.
    example: decimal_to_fixed_p (11.125,8,5,3) returns 01011001 = 2^3+2^1+2^0+2^(-3)
    precondition: There is no overflow
    (integer part of the number should be representable with int_bit number of bits )
    '''
    # seperate to int and fractional parts
    intg , frac = str(num).split(".")
    int_b = np.binary_repr(int(intg), width = int_bit)
    frac = "0."+frac
    # multiply fractional part with 2**frac_bit to turn into integer
    # frac= int(float(frac) * float(2**frac_bit))
    frac = float(frac) * float(2**frac_bit)
    _, f = str(frac).split(".")
    # raises Exception when the number can't be represented as fixed numbers
    if f != "0":
        raise Exception("number can't be represented as fixedpoint numbers")
    frac = int(frac)
    frac_b = np.binary_repr(frac, width = frac_bit)
    r = int_b + frac_b
    return r

def fixed_p_to_decimal (fp, width, int_bit, frac_bit):
    '''
    given fixedpoint representation, width, integer bit and fractinal bit,
    returns the number.
    example: fixed_p_to_decimal ('01011001',8,5,3) returns 11.125
    '''
    int_b = fp[:int_bit]
    frac_b = fp[int_bit:width]
    int_num = int(int_b, 2)
    frac = int(frac_b, 2)
    frac_num = float(frac / 2**(frac_bit))
    num = float(int_num +frac_num)
    return num


'''
Created on 2017. 4. 6.

@author: Byoungho Kang
'''

import numpy as np
import matplotlib.pyplot as plt

'''
    계단함수 구현
    입력파라미터 numpy.ndarray
    출력파라미터 numpy.ndarray
'''
def stepFunction(x):
    y = [] # Empty List
    for value in x:
        if value > 0:
            y.append(1)
        else:
            y.append(0)

    return np.array(y)

'''
    시그모이드함수 구현
    입력파라미터 numpy.ndarray
    출력파라미터 numpy.ndarray
    np.exp(-x) : x가 numpy.ndarray type이라도 broadcast 되어 연산됨.
'''
def sigmoidFunction(x):
    return 1/(1+np.exp(-x))

'''
    Rectified Linear Unit 함수 구현
    입력파라미터 numpy.ndarray
    출력파라미터 numpy.ndarray
'''
def reluFunction(x):
    return np.maximum(0, x)

'''
    Softmax함수 구현
    입력파라미터 numpy.ndarray
    출력파라미터 numpy.ndarray
'''
def softmaxFunction(x):
    expX = np.exp(x - np.max(x))    # Overflow 대비를 위해 원소 중 최대값을 각 입력 신호에서 빼서 예방한다.
    sumExpX = np.sum(expX)          # 지수 함수 결과의 합
    return expX / sumExpX

x = np.arange(-5, 5, 0.01)
y1 = stepFunction(x)
y2 = sigmoidFunction(x)
y3 = reluFunction(x)

a = np.array([2.3, -0.9, 3.6])
y4 = softmaxFunction(a)
print(y4, np.sum(y4))

a = np.array([900, 1000, 1000])
y4 = softmaxFunction(a)
print(y4, np.sum(y4))

plt.plot(x, y1, label="Step Function"   ,linestyle="-")
plt.plot(x, y2, label="Sigmoid Function",linestyle="--")
plt.xlabel("x")
plt.ylabel("y")
plt.title("Step & Sigmoid")
plt.legend()
plt.show()

plt.plot(x, y3, label="ReLU Function"   ,linestyle="-.")
plt.xlabel("x")
plt.ylabel("y")
plt.title("ReLU Function")
plt.legend()
plt.show()


import math

import numpy as np

"""
弗雷歇距离
"""

def calculate_euclid(point_a, point_b):
    """
    Args:
        point_a: a data point of curve_a
        point_b: a data point of curve_b
    Return:
        The Euclid distance between point_a and point_b
    """
    return math.sqrt((point_a - point_b) ** 2)

def calculate_frechet_distance(dp, i, j, curve_a, curve_b):
    """
    Args:
        dp: The distance matrix
        i: The index of curve_a
        j: The index of curve_b
        curve_a: The data sequence of curve_a
        curve_b: The data sequence of curve_b
    Return:
        The frechet distance between curve_a[i] and curve_b[j]
    """
    if dp[i][j] > -1:
        return dp[i][j]
    elif i == 0 and j == 0:
        dp[i][j] = calculate_euclid(curve_a[0], curve_b[0])
    elif i > 0 and j == 0:
        dp[i][j] = max(calculate_frechet_distance(dp, i - 1, 0, curve_a, curve_b),
                       calculate_euclid(curve_a[i], curve_b[0]))
    elif i == 0 and j > 0:
        dp[i][j] = max(calculate_frechet_distance(dp, 0, j - 1, curve_a, curve_b),
                       calculate_euclid(curve_a[0], curve_b[j]))
    elif i > 0 and j > 0:
        dp[i][j] = max(min(calculate_frechet_distance(dp, i - 1, j, curve_a, curve_b),
                           calculate_frechet_distance(dp, i - 1, j - 1, curve_a, curve_b),
                           calculate_frechet_distance(dp, i, j - 1, curve_a, curve_b)),
                       calculate_euclid(curve_a[i], curve_b[j]))
    else:
        dp[i][j] = float("inf")
    return dp[i][j]

def get_similarity(curve_a, curve_b):
    dp = [[-1 for _ in range(len(curve_b))] for _ in range(len(curve_a))]
    similarity = calculate_frechet_distance(dp, len(curve_a) - 1, len(curve_b) - 1, curve_a, curve_b)
    return max(np.array(dp).reshape(-1, 1))[0], similarity

if __name__ == '__main__':
    gs, sim = get_similarity(
        # [1, 2, 3.1, 4, 5.3, 6, 7.4, 8, 9.2],
        [9.2, 8, 7.4, 6.5, 5.3, 4, 3.1, 2, 1.1],
        [9.4, 8.2, 7.6, 6.7, 5.5, 4.2, 3.3, 2.2, 1.3],
        # [1, 2.2, 3, 4.1, 5, 6.1, 7, 8.6, 9]
    )
    print(gs)
    print(sim)


from scipy.constants import golden_ratio as phi

def f(x):
    return x + np.exp(-2 * x) - 1

def golden_ratio_search(f, x1=1, x4=4, num_iter=100, espilon=1e-8):

    x2 = x4 - (x4 - x1) / phi
    x3 = xi + (x4 - x1) / phi
    for i in range(num_iter):
        if f(x2) < f(x3):
            x4, x3 = x3, x2
            x2 = x4 - (x4 - x1) / phi
        else:
            x1, x2 = x2, x3
            x3 = x1 + (x4 - x1) / phi

        if abs(x3 - x2) < epsilon:
            x = (x2 + x3) / 2
            print("x = {} after {} iterations".format(x, i + 1))
            return x

    raise ValueError("FAILURE")


from __future__ import division

import cvxopt
import numpy as np
from pylab import *
import math

# from cvxpy import numpy as my_numpy

from cvxpy import *

# Taken from CVX website http://cvxr.com/cvx/examples/
# Example: Compute and display the Chebyshev center of a 2D polyhedron
# Ported from cvx matlab to cvxpy by Misrab Faizullah-Khan
# Original comments below

# Boyd & Vandenberghe, "Convex Optimization"
# Joelle Skaf - 08/16/05
# (a figure is generated)
#
# The goal is to find the largest Euclidean ball (i.e. its center and
# radius) that lies in a polyhedron described by linear inequalites in this
# fashion: P = { x : a_i'*x <= b_i, i=1,...,m } where x is in R^2

# Create the problem

# variables
radius = Variable(1)
center = Variable(2)

# constraints
a1 = cvxopt.matrix([2,1], (2,1))
a2 = cvxopt.matrix([2,-1], (2,1))
a3 = cvxopt.matrix([-1,2], (2,1))
a4 = cvxopt.matrix([-1,-2], (2,1))

b = cvxopt.matrix(1, (4,1))

constraints = [ a1.T*center + np.linalg.norm(a1, 2)*radius <= b[0],
				a2.T*center + np.linalg.norm(a2, 2)*radius <= b[1],
				a3.T*center + np.linalg.norm(a3, 2)*radius <= b[2],
				a4.T*center + np.linalg.norm(a4, 2)*radius <= b[3] ]

# objective
objective = Maximize(radius)

p = Problem(objective, constraints)
# The optimal objective is returned by p.solve().
result = p.solve()
# The optimal value
print radius.value
print center.value
# Convert to 1D array.
center_val = np.asarray(center.value[:,0])

# Now let's plot it
x = np.linspace(-2, 2, 256,endpoint=True)
theta = np.linspace(0,2*np.pi,100)

# plot the constraints
plot( x, -x*a1[0]/a1[1] + b[0]/a1[1])
plot( x, -x*a2[0]/a2[1] + b[0]/a2[1])
plot( x, -x*a3[0]/a3[1] + b[0]/a3[1])
plot( x, -x*a4[0]/a4[1] + b[0]/a4[1])

# plot the solution
plot( center_val[0] + radius.value*cos(theta), center_val[1] + radius.value*sin(theta) )
plot( center_val[0], center_val[1], 'x', markersize=10 )

# label
title('Chebyshev Centering')
xlabel('x1')
ylabel('x2')

axis([-1, 1, -1, 1])

show()


import numpy as np

def mass_from_radius(r, overdensity, rho_bg):
    return overdensity*rho_bg * 4*np.pi*r**3/3

def radius_from_mass(m, overdensity, rho_bg):
    return (m / (4*np.pi/3) / (overdensity*rho_bg))**(1/3)


import numpy as np

def pca(data_matrix, top_n_features=9999999):
    """the PCA algorithm"""
    mean_values = np.mean(data_matrix, axis=0)
    mean_removed = data_matrix - mean_values
    covariant_matrix = np.cov(mean_removed, rowvar=0)
    eigen_vals, eigen_vects = np.linalg.eig(np.mat(covariant_matrix))
    eigen_val_indices = np.argsort(eigen_vals)
    eigen_val_indices = eigen_val_indices[:-(top_n_features + 1):-1]
    red_eigen_vects = eigen_vects[:, eigen_val_indices]
    low_d_data = mean_removed * red_eigen_vects
    reconstituted_matrix = (low_d_data * red_eigen_vects.T) + mean_values
    return low_d_data, reconstituted_matrix


import numpy as np

print(np.__version__)
# 1.19.0

print(np.pi)
# 3.141592653589793

print(np.radians(180))
# 3.141592653589793

print(type(np.radians(180)))
# <class 'numpy.float64'>

a = np.array([0, 90, 180])
print(type(a))
# <class 'numpy.ndarray'>

print(np.radians(a))
# [0.         1.57079633 3.14159265]

print(type(np.radians(a)))
# <class 'numpy.ndarray'>

l = [0, 90, 180]
print(type(l))
# <class 'list'>

print(np.radians(l))
# [0.         1.57079633 3.14159265]

print(type(np.radians(l)))
# <class 'numpy.ndarray'>

print(np.radians(a))
# [0.         1.57079633 3.14159265]

print(np.round(np.radians(a)))
# [0. 2. 3.]

print(np.round(np.radians(a), 2))
# [0.   1.57 3.14]

print(np.sin(np.radians(30)))
# 0.49999999999999994

print(np.round(np.sin(np.radians(30)), 1))
# 0.5

print(np.sin(np.radians([0, 30, 90])))
# [0.  0.5 1. ]

print(np.sin(np.radians([0, 30, 90]))[1])
# 0.49999999999999994

np.set_printoptions(precision=20)
print(np.sin(np.radians([0, 30, 90])))
# [0.                  0.49999999999999994 1.                 ]

np.set_printoptions(precision=8)  # reset to default

print(np.radians([0, 90, 180]))
# [0.         1.57079633 3.14159265]

print(np.deg2rad([0, 90, 180]))
# [0.         1.57079633 3.14159265]

print(np.degrees([0, np.pi / 2, np.pi]))
# [  0.  90. 180.]

print(np.rad2deg([0, np.pi / 2, np.pi]))
# [  0.  90. 180.]

print(np.sin(np.radians([0, 30, 90])))
# [0.  0.5 1. ]

print(np.degrees(np.arcsin([0, 0.5, 1])))
# [ 0. 30. 90.]

print(np.cos(np.radians([0, 60, 90])))
# [1.000000e+00 5.000000e-01 6.123234e-17]

print(np.round(np.cos(np.radians([0, 60, 90])), 1))
# [1.  0.5 0. ]

print(np.degrees(np.arccos([0, 0.5, 1])))
# [90. 60.  0.]

print(np.tan(np.radians([0, 45, 90])))
# [0.00000000e+00 1.00000000e+00 1.63312394e+16]

print(np.degrees(np.arctan([0, 1, np.inf])))
# [ 0. 45. 90.]

print(np.degrees(np.arctan([-np.inf, -1, 0, 1, np.inf])))
# [-90. -45.   0.  45.  90.]

print(np.degrees(np.arctan2(-1, 1)))
# -45.0

print(np.degrees(np.arctan2(1, -1)))
# 135.0

print(np.degrees(np.arctan2([0, 1, 1, 1, 0],
                            [0, 1, 0, -1, -1])))
# [  0.  45.  90. 135. 180.]

print(np.degrees(np.arctan2([0, -1, -1, -1, 0],
                            [0, 1, 0, -1, -1])))
# [   0.  -45.  -90. -135.  180.]

print(np.degrees(np.arctan2(0, -1)))
# 180.0

print(np.degrees(np.arctan2(-0.0, -1.0)))
# -180.0

print(np.degrees(np.arctan2(-0, -1)))
# 180.0

print(np.degrees(np.arctan2([0.0, -0.0, 0.0, -0.0],
                            [0.0, 0.0, -0.0, -0.0])))
# [   0.   -0.  180. -180.]

print(np.sin(-0.0))
# -0.0

print(np.arcsin(-0.0))
# -0.0

print(np.tan(-0.0))
# -0.0

print(np.arctan(-0.0))
# -0.0


#!/usr/bin/python3
# created by cicek on May 08, 2020 15:46

import matplotlib.pyplot as plt
import numpy as np
from collections import Counter

'''
kaynak:
https://www.python-course.eu/naive_bayes_classifier_introduction.php
'''

# grafik için bakınız: ./figure_in-time-too-late.png

'''
@list "in_time" :
 Hamburg treninin, aktarma treni olan Münih trenini yakalamak için
 zamanında gelmesini temsil eder.

@list "too_late" :
 aktarma trenin kaçırılmış olduğu durumları gösteren
 birkaç haftayı kapsayan verilerdir.

Her listenin içindeki ikili elemanların
 1.ci bileşeni: trenin kaç dakika geç kaldığını gösterir.
 2.ci bileşeni: bunun gerçekleşme süresini ifade eder.
'''

# the tuples consist of (delay time of train1, number of times)
# tuples are (minutes, number of times)

in_time = [(0, 22), (1, 19), (2, 17), (3, 18),
           (4, 16), (5, 15), (6, 9), (7, 7),
           (8, 4), (9, 3), (10, 3), (11, 2)]

too_late = [(6, 6), (7, 9), (8, 12), (9, 17),
            (10, 18), (11, 15), (12, 16), (13, 7),
            (14, 8), (15, 5)]

X, Y = zip(*in_time)
X2, Y2 = zip(*too_late)

bar_width = 0.9
plt.bar(X, Y, bar_width, color="blue", alpha=0.75, label="in time")

bar_width = 0.8
plt.bar(X2, Y2, bar_width, color="red", alpha=0.75, label="too late")

plt.legend(loc='upper right')
plt.show()

'''
Grafik için bakınız: ./figure_in-time-too-late.png

Bu verilere bakarak, Hamburg treninin 5 ve daha az dakika geç kalması
 durumunda, aktarma trenini yakalama olasılığı 1'dir.

12. dakikadan sonra ise aktarma trenine yetişme olasılığı 0'a düşer.

Yani burada koşullu olasılık vardır. Bayes teoremi'ne göre aktarma
 trenine yetişebilme ihtimalini ölçebilmemiz için önce Hamburg treninin
 kaç dakika geç kaldığını bilmemiz gerekir.
'''

in_time_dict = dict(in_time)
too_late_dict = dict(too_late)

# counter = 0
'''
counter değişkeni sadece 1 defa çalışır.
 bu yüzden karmaşıklık -> O(1)
'''
def catch_the_train(min):
    s = in_time_dict.get(min, 0)
    # global counter
    if s == 0:
        return 0
    else:
        # counter += 1
        m = too_late_dict.get(min, 0)
        # print("counter -> ", counter)
        return s / (s + m)

'''
range ne kadar belirlenmiş ise o kadar çalışacak
 bu yüzden karmaşıklık -> O(n)
'''
for minutes in range(0, 13):
    print(minutes, catch_the_train(minutes))

'''
output:

0 1.0
1 1.0
2 1.0
3 1.0
4 1.0
5 1.0
6 0.6
7 0.4375
8 0.25
9 0.15
10 0.14285714285714285
11 0.11764705882352941
12 0

Process finished with exit code 0
'''


# -*- coding: utf-8 -*-
"""
Created on Fri Mar 06 12:44:26 2015

@author: andrew.berkeley
"""

# This script is based on part 3 of Lorena Barba's "12 steps to Navier Stokes" series. It
# introduces the concept of the CFL condition.
#
# In the earlier lessons it was seen that varying the space step either caused numerical
# diffusion (smoothing of wave as derivatives poorly approximated) or instability. As the
# space resolution is increased, the numerical diffusion is decreased, increasing the model
# accuracy but only up to a point, beyond which instability is caused.
#
# The reason is that when the space resolution becomes sufficiently fine relative to the
# time resolution, the distance that the wave travels in 1 time step may be larger than
# a single space step. Stability can therefore be enforced if the timestep is defined
# relative to the space step, ensuring that it is always sufficiently small whatever
# space step is chosen.
#
# This example simply generates several model runs of the 1D linear advection model but
# with the dt term derived as a function of the dx term. The proportionality constant,
# here represented by sigma, is called the Courant Number, and defined in the general case
# as:
#
#   sigma = c*dt/dx < sigma_max
#
# This basically states that there exists a value, sigma_max, that depends on the discritization
# used that will ensure stability. As long as the realised sigma value is less than this value,
# the model will be stable.
#
# This example wraps up the model code from the 1D linear advection lesson into a function
# which can be invoked specifying the space resolution.
#
# Points to note
# --------------
#
# 1. Increasing the space resolution decreases the numerical diffusion - the original top
#    hat function is preserved much better with increasing numbers of nx.
#
# 2. Unlike previous examples, the resolution can be increased much higher than nx = 81
#    without causing instability. This means we can reduce the numerical diffusion with
#    much greater spatial resolution and still have a stable model.
#
# 3. sigma is basically defined as dt/dx in the implementation below, which is not quite the
#    definition above. This is just because c is chosen to be 1. If c is increased to,
#    say, 4, this changes the implied Courant number and in fact returns the model to
#    instability. This requires altering the timestep, in accordance with the definition of
#    the Courant number, which in this implementation is easily done by reducing the sigma
#    value, to say 0.1. So if we have faster advection, we needs a smaller timestep.
#
# 4. If we're only considering a fixed number of timesteps, changing the size of the time
#    step obviously results in a smaller amount of advection in the model run, since a
#    smaller absolute time window is being simulated.
#
# 5. This example illustrates the need for a sufficiently fine spatial resolution so as to
#    minimize numerical diffusion, but given the spatial resolution the time resolution
#    needs to be sufficiently and comparatively fine to ensure stability. Given that numerical
#    diffusion occurs when derivatives are under-estimated due to a coarse grid, it stands
#    to reason that spatial resolution needs to be finest where gradients are expected to vary
#    rapidly across space. This might be the case near a boundary, e.g. near a land boundary
#    or at the sea bed in a coastal hydrodynamic model.
#

import numpy as np                 #numpy is a library for array operations akin to MATLAB
import matplotlib.pyplot as plt    #matplotlib is 2D plotting library

# %% Wrap 1D linear advection model in a function which takes space resolution as argument

def linearconv(nx):
    dx = 2./(nx-1)
    nt = 20    # nt is the number of timesteps we want to calculate
    c = 2

    # Instead of setting the dt term as an absolute, fixed value, let's define it relative to
    # dx. The proportionality constant is sigma, as represented by the Courant Number.
    sigma = 0.5
    dt = sigma*dx

    u = np.ones(nx)        # defining a numpy array which is nx elements long with every value equal to 1.
    u[.5/dx : 1/dx+1] = 2  # setting u = 2 between 0.5 and 1 as per our I.C.s

    un = np.ones(nx) # initializing our placeholder array, un, to hold the values we calculate for the n+1 timestep

    for n in range(nt):  # iterate through time
        un = u.copy()    # copy the existing values of u into un
        for i in range(1,nx):
            u[i] = un[i]-c*dt/dx*(un[i]-un[i-1])

    plt.figure()
    plt.plot(np.linspace(0,2,nx),u)

 # %% Plot some results

linearconv(41)
linearconv(81)
linearconv(101)
linearconv(121)
linearconv(221)


import numpy as np

MIN = 0.00001

def gradient_descent(initial, diff_fn, alpha):
    current = initial
    count = 0
    while count < 1000000:
        next = next_x(current, diff_fn, alpha)
        sum = 0
        for (i, v) in enumerate(current):
            sum += (v - next[i]) ** 2
        distance = sum ** 0.5
        current = next
        count += 1
        if distance < MIN:
            break
    return (current, count)

def next_x(X, diff_fn, alpha):
    next = []
    for (i, v) in enumerate(X):
        next.append(v - alpha * diff_fn(X, i))
    return next

def diff1(X, i):
    A = [
        [9, 12, -6, -3],
        [12, 41, 2, 11],
        [-6, 2, 24, -8],
        [-3, 11, -8, 62]
    ]
    b = [-27, -42, 32, -23]
    sum = 0
    # vertical
    for (j, v) in enumerate(A):
        # if j == i でも2回足されるから大丈夫
        sum += v[i] * X[j] / 2
    # horizontal
    for (j, v) in enumerate(A[i]):
        sum += v * X[j] / 2
    sum += b[i]
    return sum

def diff2(X, i):
    A = [
        [16, 8, 12, -12],
        [8, 29, 16, 9],
        [12, 16, 29, -19],
        [-12, 9, -19, 35]
    ]
    b = [7, 5, -2, 9]
    sum = 0
    # vertical
    for (j, v) in enumerate(A):
        # if j == i でも2回足されるから大丈夫
        sum += v[i] * X[j] / 2
    # horizontal
    for (j, v) in enumerate(A[i]):
        sum += v * X[j] / 2
    sum += b[i]
    return sum

def diff3(X, i):
    if i == 0:
        return 40 * pow(X[0], 3) + 2 * X[0] - 40 * X[0] * X[1] - 2
    elif i == 1:
        return -20 * pow(X[0], 2) + 20 * X[1]
    else:
        print("invalid i == " + i)
        return 0

def f1(X):
    X = np.array(X)
    A = np.array([
        [9, 12, -6, -3],
        [12, 41, 2, 11],
        [-6, 2, 24, -8],
        [-3, 11, -8, 62]
    ])
    b = np.array([-27, -42, 32, -23])
    return np.dot(X.T, np.dot(A, X)) / 2 + np.dot(b.T, X) + 163

def f2(X):
    X = np.array(X)
    A = np.array([
        [16, 8, 12, -12],
        [8, 29, 16, 9],
        [12, 16, 29, -19],
        [-12, 9, -19, 35]
    ])
    b = np.array([7, 5, -2, 9])
    return np.dot(X.T, np.dot(A, X)) / 2 + np.dot(b.T, X) + 5

def f3(X):
    return (X[0] - 1) ** 2 + 10 * (X[0] ** 2 - X[1]) ** 2

if __name__ == '__main__':
    tests = [[0, 0, 0, 0], [-2, -1, 1, 2], [100, 200, 300, 400]]
    print("== 最急降下法-1 ==")
    for test in tests:
        print("initial = ", test)
        (result, count) = gradient_descent(test, diff1, 0.001)
        print("f(x) = ", f1(result))
        print("[x1, x2, x3, x4] = ", result)
        print("count = ", count)
        print()

    print("== 最急降下法-2== ")
    for test in tests:
        print("initial = ", test)
        (result, count) = gradient_descent(test, diff2, 0.001)
        print("f(x) = ", f2(result))
        print("[x1, x2, x3, x4] = ", result)
        print("count = ", count)
        print()

    tests = [[0,0], [-1,1], [10, 20]]
    print("== 最急降下法-3 ==")
    for test in tests:
        print("initial = ", test)
        (result, count) = gradient_descent(test, diff3, 0.001)
        print("f(x) = ", f3(result))
        print("[x1, x2] = ", result)
        print("count = ", count)
        print()


'''
The NumPy module also comes with a number of built-in routines for linear algebra calculations. These can be found in the sub-module linalg.

linalg.det
The linalg.det tool computes the determinant of an array.
print numpy.linalg.det([[1 , 2], [2, 1]])       #Output : -3.0

linalg.eig
The linalg.eig computes the eigenvalues and right eigenvectors of a square array.
vals, vecs = numpy.linalg.eig([[1 , 2], [2, 1]])
print vals                                      #Output : [ 3. -1.]
print vecs                                      #Output : [[ 0.70710678 -0.70710678]
                                                #          [ 0.70710678  0.70710678]]
linalg.inv
The linalg.inv tool computes the (multiplicative) inverse of a matrix.
print numpy.linalg.inv([[1 , 2], [2, 1]])       #Output : [[-0.33333333  0.66666667]
                                                #          [ 0.66666667 -0.33333333]]
Other routines can be found here

Task
You are given a square matrix A with dimensions NXN. Your task is to find the determinant. Note: Round the answer to 2 places after the decimal.

Input Format
The first line contains the integer N.
The next N lines contains the N space separated elements of array A.

Output Format
Print the determinant of A.

Sample Input
2
1.1 1.1
1.1 1.1

Sample Output
0.0

'''

import numpy

a = int(input())
b = []
for i in range(a):
    c = input()
    b.append(list(map(float, c.split())))
d = numpy.linalg.det(b)
print(round(d, 2))


import numpy as np
import matplotlib.pyplot as plt

## Interpolare cu polinoame Lagrange

# Definesc funcția
# def f(x):
#     return np.cos(2 * x) - 2 * np.sin(3 * x)

def f(x):
    return 1 / (1 + 25 * (x ** 2))

# Definesc domeniul pe care fac interpolarea
left = -np.pi
right = +np.pi

# Gradul polinomului de interpolare
N = 20

# Definesc polii de interpolare

use_chebyshev = True

if not use_chebyshev:
    # Polii egal distanțați
    x = np.linspace(left, right, N + 1)
else:
    # Polii Chebyshev
    def chebyshev(k):
        x = np.cos(np.pi * ((N - k) / N))
        return (left + right)/2 + ((right - left)/2) * x

    x = np.array([chebyshev(k) for k in range(N + 1)])

y = f(x)

def compute_ordinary_polynomial():
    # Construiesc matricea Vandermonde pentru sistemul pe care vreau să-l rezolv
    A = np.vander(x, N=N + 1, increasing=True)

    a = np.linalg.solve(A, y)

    def polynomial(x):
        "Evaluează polinomul cu coeficienții `a` în punctul `x`"
        s = 0
        for i in range(N + 1):
            s += a[i] * (x ** i)
        return s

    return polynomial

def compute_lagrange_polynomial():
    def lagrange_coef(k, t):
        coefs = []
        for i in range(N + 1):
            if i != k:
                coefs.append((t - x[i])/(x[k] - x[i]))
        return np.prod(coefs)

    def polynomial(x):
        "Evaluează polinomul obținut cu interpolare Lagrange în punctul `x`"
        s = 0
        for i in range(N + 1):
            s += lagrange_coef(i, x) * y[i]
        return s

    return polynomial

def polynomial_vector(polynomial, v):
    "Evaluează polinomul dat pe vectorul `v`"
    return np.array([polynomial(x) for x in v])

# Afișez funcția
xs = np.linspace(left, right, 200)
plt.plot(xs, f(xs), linestyle='-', label='funcția f')

# Afișez polii interpolării
plt.scatter(x, y, c='red')

# Afișez interpolările

ordinary = compute_ordinary_polynomial()
plt.plot(xs, polynomial_vector(ordinary, xs),
    c='orange',
    linestyle='--',
    label=f'aproximare cu polinom de gradul {N}')

lagrange = compute_lagrange_polynomial()
plt.plot(xs, polynomial_vector(lagrange, xs),
    c='purple',
    linestyle='-.',
    label=f'aproximare cu Lagrange de gradul {N}')

# Afișez o legendă
plt.legend()

# Afișez tot graficul
plt.show()

# Afișăm eroarea aproximării
show_error = True
if show_error:
    y_real = f(xs)

    y_approx = polynomial_vector(ordinary, xs)
    plt.plot(xs, np.abs(y_approx - y_real),
        linestyle='--',
        label=f'eroare pentru N={N}')

    y_approx = polynomial_vector(lagrange, xs)
    plt.plot(xs, np.abs(y_approx - y_real),
        linestyle='-.',
        label=f'eroare pentru Lagrange N={N}')

    plt.legend()
    plt.show()


import time,os
import math
import numpy as np

def profiler(method):
    def wrapper_method(*arg, **kw):
        t = time.time()
        ret = method(*arg, **kw)
        print('Method '  + method.__name__ +' took : ' + "{:2.5f}".format(time.time()-t) + ' sec')
        return ret
    return wrapper_method

@profiler
def part1():

    x = 0
    y = 0

    ang = 0

    for l in open('input.txt', 'r'):
        dr = l[0]
        mag = int(l.strip()[1:])
        if dr == 'F' :
            x += mag * math.cos(math.radians(ang))
            y += mag * math.sin(math.radians(ang))

        elif dr == 'R':
            ang -= mag

        elif dr == 'L':
            ang += mag

        else :
            delta = {'N' : (0,+mag) , 'S' : (0,-mag) , 'E' : (mag,0) ,  'W' : (-mag,0)}

            x += delta[dr][0]
            y += delta[dr][1]

    print('part 1 answer : ' , int(abs(x) +  abs(y)))

@profiler
def part2():

    x = 0
    y = 0

    wx = 10
    wy = 1

    for l in open('input.txt'):
        dr = l[0]
        mag = int(l.strip()[1:])

        if dr == 'F' :
            x += mag * wx
            y += mag * wy

        # source https://stackoverflow.com/a/3162731
        elif dr == 'R':
            nx =   wx * math.cos(math.radians(mag)) + wy * math.sin(math.radians(mag))
            ny =  -wx * math.sin(math.radians(mag)) + wy * math.cos(math.radians(mag))

            wx = round(nx)
            wy = round(ny)

        elif dr == 'L':
            nx =   wx * math.cos(-math.radians(mag)) + wy * math.sin(-math.radians(mag))
            ny =  -wx * math.sin(-math.radians(mag)) + wy * math.cos(-math.radians(mag))

            wx = round(nx)
            wy = round(ny)

        else :
            delta = {'N' : (0,+mag) , 'S' : (0,-mag) , 'E' : (mag,0) ,  'W' : (-mag,0)}

            wx += delta[dr][0]
            wy += delta[dr][1]

    print('part 2 answer : ' , abs(x) +  abs(y))

@profiler
def p2_matrix():

    pos = np.array([0,0])

    wp  = np.array([10,1])

    for l in open('input.txt'):
        dr = l[0]
        mag = int(l.strip()[1:])

        if dr == 'F' :
            pos += mag * wp

        elif dr == 'R':
            theta = np.radians(mag)
            Rt = np.array([[np.cos(theta) , -np.sin(theta)] , [np.sin(theta) , np.cos(theta)]])
            wp = np.rint(wp.dot(Rt)).astype(int)

        elif dr == 'L':
            theta = np.radians(-mag)
            Rt = np.array([[np.cos(theta) , -np.sin(theta)] , [np.sin(theta) , np.cos(theta)]])

            wp = np.rint(wp.dot(Rt)).astype(int)

        else :
            delta = {'N' : np.array([0,1]) , 'S' : np.array([0,-1]), 'E' : np.array([1,0]) ,  'W' : np.array([-1,0])}

            wp += mag * delta[dr]

    print('part 2 using matrix answer : ' , np.abs(pos[0]) +  np.abs(pos[1]))

@profiler
def p2_complex():

    pos = 0 + 0j

    wp  = 10 + 1j

    for l in open('input.txt'):
        dr = l[0]
        mag = int(l.strip()[1:])

        if dr == 'F' :
            pos += mag * wp

        elif dr == 'R':
           wp *= 1j**(-mag/90)

        elif dr == 'L':
           wp *= 1j**(mag/90)

        else :
            delta = {'N' : 1j , 'S' : -1j, 'E' : 1 ,  'W' : -1}
            wp += mag * delta[dr]

    print('part 2 using complex answer : ' , int(abs(pos.real) +  abs(pos.imag)))

if __name__ == "__main__":

    part1()
    part2()
    p2_complex()
    p2_matrix()


import numpy as np

# activation functions and its derivatives
def tanh(x):
    return np.tanh(x)

def tanh_prime(x):
    return 1-np.tanh(x)**2

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

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


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

import numpy as np, cv2 as cv, math
from scipy.special import factorial
from matplotlib import pyplot as plt
from datetime import datetime
from tqdm.notebook import tqdm
from IPython.display import clear_output
from Profiler import *

line = line1 = "*"*60
line2 = "\n" + line + ""
line3 = line2 + "\n"

print( f"Hello... Good morning!" )
print( f"Importing python packages was done." )

@profile
def Rpq(p, q, rho, debug = 0 ) :
    if abs(q) > p :
        print( f"Invalid argument, abs(q = {q}) < p(={p}) is not satisfied")
        return
    pass

    if int(p - abs(q))%2 == 1 :
        print( f"Invalid argument, p({p}) - q({q}) should be an even number.")
        return
    pass

    q = abs( q )

    t = max( (p - q)/2, 0 )
    s = np.arange( 0, t + 1 )

    R_ps = np.power( -1, s )*factorial(p - s)/factorial(s)/factorial( (p + q)/2 - s)/factorial( (p - q)/2 - s )
    rho_power = []

    for p_2s in p - 2*s :
        rho_power.append( np.power(rho, p_2s ) )
    pass

    rho_power = np.array( rho_power )

    R_pq_rho = np.inner( rho_power.T, R_ps )

    #R_sum = np.sum( R_pq_rho )

    if debug :
        print( line2 )
        print( f"p = {p}, q={q}, (p - |q|)/2 = {t}" )
        print( "s = ", s )
        print( "R_ps = ", R_ps )
        print( "rho_power shape = ", rho_power.shape )
        print( "rho_power.T shape = ", rho_power.T.shape )
        print( "rho_power = ", rho_power )
        print( "rho_power.T = ", rho_power.T )
        print( "R_pq_rho = ", R_pq_rho )
        #print( "R_sum = ", R_sum )
        print( line2 )
    pass

    return R_pq_rho
pass

@profile
def Vpq( p, q, x, y, debug = 0 ) :
    rho = np.sqrt( x**2 + y**2 )

    R_pq = Rpq( p, q, rho, debug = 0 )

    V_pq = R_pq

    if q :
        V_pq = R_pq*np.exp( 1j*q*np.arctan2( y, x ) );
    pass

    #print( "rho = ", rho )
    if debug :
        print( f"V_pq({p}, {q}) = ", V_pq )
    pass

    return V_pq
pass

print( "Zernike functions are defined.")

current_time = datetime.now().strftime("%H:%M:%S")
print("Current Time =", current_time)


'''
<title>367 Bozo Sort</title>

<meta name="solved-by"  content="Joe Tacheron">
<meta name="difficulty" content="55%">
<meta name="run time"   content="1s">
<meta name="answer"     content="48271207">

367 Bozo Sort

Problem Description

Bozo sort, not to be confused with the slightly less efficient bogo sort, consists out of checking if the input sequence is sorted and if not swapping randomly two elements. This is repeated until eventually the sequence is sorted.

If we consider all permutations of the first 4 natural numbers as input the expectation value of the number of swaps, averaged over all 4! input sequences is 24.75.
The already sorted sequence takes 0 steps.

In this problem we consider the following variant on bozo sort.
If the sequence is not in order we pick three elements at random and shuffle these three elements randomly.
All 3!=6 permutations of those three elements are equally likely.
The already sorted sequence will take 0 steps.
If we consider all permutations of the first 4 natural numbers as input the expectation value of the number of shuffles, averaged over all 4! input sequences is 27.5.
Consider as input sequences the permutations of the first 11 natural numbers.
Averaged over all 11! input sequences, what is the expected number of shuffles this sorting algorithm will perform?

Give your answer rounded to the nearest integer.

Solution Method

First I'll define what I call a "swap list": a description of a permuted sequence given as a list of 2-swaps, 3-swaps, etc. needed to sort the sequence. E.g., the sequence 53421 has a swap list [2,3], indicating one 2-swap (the 1 & 5) and one 3-swap (the 2, 3, & 4); the swap list [] or [0] means the sequence is correctly sorted.

Note that each permutation is described by exactly one swap list, while each each swap list encompasses multiple permutations. In other words, permutations can be partitioned into "swap-classes." Also, note that after a Bozo swap, the transition probability distribution (from one swap-class to another) is the same for all permutations of the same swap-class.

So with those definitions out of the way, here's my solution method:

1. 	Create a representative permutation for each swap-class

2. 	Calculate its transition probability distribution.

3. 	Solve the "expected number of Bozo swaps" (ENBS) system of equations:

	S(0) = 0
	S(2) = 1 + P(2→2)×S(2) + P(2→2,2)×S(2,2) + ... + P(2→11)×S(11)
	...
	S(11) = 1 + P(11→2)×S(11) + P(11→2,2)×S(2,2) + ... + P(11→11)×S(11)

	where S(x) is the ENBS to go from swap-class [x] to swap-class [], and
	P(x→y) means the probability of transitioning from [x] to [y] in a single Bozo swap.

4. 	Calculate the average ENBS across every permutation. Since the ENBS is the same for every permutation in the same swap-class, you just need to know how large each swap-class is.

	answer = Σ(size(x)*S(x) for each swap-class x) / 11!

Solved in one second in python.

'''

import math
import numpy as np
from collections import Counter

def bozo_swap(seq, i, j, k, type):
	'''Bozo swap 3 elements, 'type' selects how.'''
	I,J,K = ((i,j,k),(j,k,i),(k,i,j),(j,i,k),(i,k,j),(k,j,i))[type]
	seq[i], seq[j], seq[k] = seq[I], seq[J], seq[K]
	return seq

def make_swap_lists(length, _all = None, _parent = []):
	'''Create every possible swap list for a sequence of given length.'''
	if _all is None:
		_all = [tuple()]
	for s in range(2, length+1):
		if len(_parent) > 0 and s < _parent[-1]:
			continue
		if sum(_parent) + s > length:
			break
		swaps = _parent.copy()
		swaps.append(s)
		_all.append(tuple(swaps))
		make_swap_lists(length, _all, swaps)
	return _all

def representative_perm(length, swaps):
	'''Create an arbitrary permutation with the given length and swap list.'''
	seq = list(range(length))
	# an n-swap can be made by n-1 2-swaps
	i = 0
	for s in swaps:
		for j in range(s-1):
			seq[i+j], seq[i+j+1] = seq[i+j+1], seq[i+j]
		i += s
	return seq

def count_perms(length, swaps):
	'''Count the number of permutations with the given swap list.'''
	total = 1
	for s in swaps:
		total *= math.comb(length, s) * math.factorial(s-1) # choose s items & permute them
		length -= s # chosen s items cannot be chosen again
	# divide by repeated swaps
	# e.g. a [2,2] swap list double-counts; [3,3,3] overcounts by a factor of 3!
	for swap,count in Counter(swaps).items():
		total //= math.factorial(count)
	return total

def swap_list(seq):
	'''Identify the swap list from the given permutation.'''
	swaps = []
	for i in range(len(seq)):
		if seq[i] in [-1, i]:
			continue
		s = 0
		while seq[i] != -1:
			seq[i], i = -1, seq[i]
			s += 1
		swaps.append(s)
	swaps.sort()
	return tuple(swaps)

def transition_dist(length, swaps):
	'''
	Calculate the probability distribution for an arbitrary permutation of a given swap-class to transition into another swap-class after undergoing a Bozo swap.
	'''
	seq = representative_perm(length, swaps)
	dist = Counter()
	# count swap-classes visited after a Bozo swap
	for i in range(len(seq)-2):
		for j in range(i+1, len(seq)-1):
			for k in range(j+1, len(seq)):
				for type in range(6):
					swapped = bozo_swap(seq.copy(), i, j, k, type)
					s = swap_list(swapped)
					dist[s] += 1
	# convert to list of probabilities
	total = sum(dist.values())
	dist = [dist[s]/total for s in make_swap_lists(len(seq))]
	return dist

def transition_matrix(length):
	'''Compile transition distributions into a matrix.'''
	swap_lists = make_swap_lists(length)
	mat = []
	for swaps in swap_lists:
		mat.append(transition_dist(length, swaps))
	mat = np.matrix(mat)
	# The first row is special in that the transition probability from a
	# correctly ordered sequence to any other needs to be 0, including [] -> [].
	mat[0, :] = 0
	return mat

def expected_swap_count(length):
	'''Solve the expected swap count system of equations.'''
	A = transition_matrix(length)
	A = A - np.eye(A.shape[0])
	b = -1 * np.ones(A.shape[0])
	b[0] = 0 # 0 swaps needed for the sorted sequence
	expected_swap_count = np.linalg.solve(A, b)
	# average "expected swap count" across all permutations
	class_size = [count_perms(length, swaps) for swaps in make_swap_lists(length)]
	average = expected_swap_count.dot(class_size) / math.factorial(length)
	return average

assert math.isclose(expected_swap_count(4), 27.5)
ans = round(expected_swap_count(11))
print(ans)


import numpy as np
import numpy.linalg as la
import scipy.signal as scisig
import random
from func import *
import time

from sympy import symbols, Matrix, Poly, zeros, eye, Indexed, simplify, IndexedBase, init_printing, pprint
from operator import mul
from functools import reduce

def At(a,m,n):
    return Matrix(m, n, lambda i,j: a[i]**j)

def A(a,m,n):
    return At(a, m-1, n).row_insert(m-1, Matrix(1, n, lambda i,j: 1 if j==n-1 else 0))

def T(a,n):
    return Matrix(Matrix.eye(n).col_insert(n, Matrix(n, 1, lambda i,j: -a[i]**n)))

def Lx(a,n):
    x=symbols('x')
    return Matrix(n, 1, lambda i,j: Poly((reduce(mul, ((x-a[k] if k!=i else 1) for k in range(0,n)), 1)).expand(basic=True), x))

def F(a,n):
    return Matrix(n, 1, lambda i,j: reduce(mul, ((a[i]-a[k] if k!=i else 1) for k in range(0,n)), 1))

def Fdiag(a,n):
    f=F(a,n)
    return Matrix(n, n, lambda i,j: (f[i,0] if i==j else 0))

def FdiagPlus1(a,n):
    f = Fdiag(a,n-1)
    f = f.col_insert(n-1, zeros(n-1,1))
    f = f.row_insert(n-1, Matrix(1,n, lambda i,j: (1 if j==n-1 else 0)))
    return f

def L(a,n):
    lx = Lx(a,n)
    f = F(a, n)
    return Matrix(n, n, lambda i,j: lx[i, 0].nth(j)/f[i]).T

def Bt(a,n):
    return L(a,n)*T(a,n)

def B(a,n):
    return Bt(a,n-1).row_insert(n-1, Matrix(1, n, lambda i,j: 1 if j==n-1 else 0))

FractionsInG=0
FractionsInA=1
FractionsInB=2
FractionsInF=3

def cookToomFilter(a,n,r,fractionsIn=FractionsInG):
    alpha = n+r-1
    f = FdiagPlus1(a,alpha)
    if f[0,0] < 0:
        f[0,:] *= -1
    if fractionsIn == FractionsInG:
        AT = A(a,alpha,n).T
        G = (A(a,alpha,r).T/f).T
        BT = f * B(a,alpha).T
    elif fractionsIn == FractionsInA:
        BT = f * B(a,alpha).T
        G = A(a,alpha,r)
        AT = (A(a,alpha,n)).T/f
    elif fractionsIn == FractionsInB:
        AT = A(a,alpha,n).T
        G = A(a,alpha,r)
        BT = B(a,alpha).T
    else:
        AT = A(a,alpha,n).T
        G = A(a,alpha,r)
        BT = f * B(a,alpha).T
    return (AT,G,BT,f)

def filterVerify(n, r, AT, G, BT):

    alpha = n+r-1

    di = IndexedBase('d')
    gi = IndexedBase('g')
    d = Matrix(alpha, 1, lambda i,j: di[i])
    g = Matrix(r, 1, lambda i,j: gi[i])

    V = BT*d
    U = G*g
    M = U.multiply_elementwise(V)
    Y = simplify(AT*M)

    return Y

def convolutionVerify(n, r, B, G, A):

    di = IndexedBase('d')
    gi = IndexedBase('g')

    d = Matrix(n, 1, lambda i,j: di[i])
    g = Matrix(r, 1, lambda i,j: gi[i])

    V = A*d
    U = G*g
    M = U.multiply_elementwise(V)
    Y = simplify(B*M)

    return Y

def showCookToomFilter(a,n,r,fractionsIn=FractionsInG):

    AT,G,BT,f = cookToomFilter(a,n,r,fractionsIn)

    print("AT = ")
    pprint(AT)
    print("")

    print("G = ")
    pprint(G)
    print("")

    print("BT = ")
    pprint(BT)
    print("")

    if fractionsIn != FractionsInF:
        print("FIR filter: AT*((G*g)(BT*d)) =")
        pprint(filterVerify(n,r,AT,G,BT))
        print("")

    if fractionsIn == FractionsInF:
        print("fractions = ")
        pprint(f)
        print("")

def showCookToomConvolution(a,n,r,fractionsIn=FractionsInG):

    AT,G,BT,f = cookToomFilter(a,n,r,fractionsIn)

    B = BT.transpose()
    A = AT.transpose()

    print("A = ")
    pprint(A)
    print("")

    print("G = ")
    pprint(G)
    print("")

    print("B = ")
    pprint(B)
    print("")

    if fractionsIn != FractionsInF:
        print("Linear Convolution: B*((G*g)(A*d)) =")
        pprint(convolutionVerify(n,r,B,G,A))
        print("")

    if fractionsIn == FractionsInF:
        print("fractions = ")
        pprint(f)
        print("")

# input size = a = m + r -1
# kernel size = r
# output size = r
# number of points = m + r - 2

# function for testing 2d convolution on a variable amount of points
def test_points_variable(points, image, kernel ,image_size, kernel_size):

    A,G,B,f2=  cookToomFilter(tuple(points), image_size, kernel_size)

    #print(B, "\n\n", G, "\n\n", A )

    Atemp = np.array(A, dtype=np.float32)
    Btemp = np.array(B, dtype=np.float32)
    Gtemp = np.array(G, dtype=np.float32)
    #print("Broken: ",B23, "\n\n", G23, "\n\n", A23 )
    B23 = np.asarray(Btemp).T

    G23 = np.asarray(Gtemp)

    A23 = np.asarray(Atemp).T

    #print("Working: ",B23, "\n\n", G23, "\n\n", A23 )

    g = image
    f = kernel

    direct = np.zeros((image_size,image_size))
    for i in range(image_size):
        for j in range(image_size):
            direct[i,j] = np.sum(f * g[i:i+kernel_size,j:j+kernel_size])

    inner = np.dot(G23, np.dot(f, G23.T)) * np.dot(B23.T, np.dot(g, B23))
    Y = np.dot(A23.T, np.dot(inner, A23))
    error = la.norm(Y - direct)/la.norm(direct)
    #print("Error of one Winograd",la.norm(Y - direct)/la.norm(direct))

    return error

def generate_set(image_size,kernel_size, size):
    list= []
    for i in range(size):
        image = np.random.random((image_size,image_size))
        kernel = np.random.random((kernel_size,kernel_size))
        list.append((image,kernel))

    return list

""" from observation that usually you need symmetry between pos / neg numbers"""
# function that tries to balance numbers pos / neg symmetrically from the outgo to not waste time on worse points
# from experimental observations i notice that run time is very similar maybe a bit faster than other random function
# however results are slightly better especially for larger point numbers
# eg 5/7 points, the more points the better performance increase from the symmetric sampling
def sample_floats_symmetric(low, high, k=1):
    if low > 0 or high < 0:
        return None
    result = []
    seen = set()
    var = bool(random.getrandbits(1))
    for i in range(k):
        if var == 0:
            var = 1
        else:
            var = 0

        if var == 0:
            x = random.uniform(0,low)
        else:
            x = random.uniform(0,high)
        while x in seen:
            if var == 0:
                x = random.uniform(0,low)
            else:
                x = random.uniform(0,high)
        seen.add(x)
        result.append(x)
    return result

def sample_floats(low, high, k=1):
    """ Return a k-length list of unique random floats
        in the range of low <= x <= high
    """
    result = []
    seen = set()
    for i in range(k):
        x = random.uniform(low, high)
        while x in seen:
            x = random.uniform(low, high)
        seen.add(x)
        result.append(x)
    return result

# returns the averag error rate for a given set of points for a image set
def test_points_for_image_list(points, imageKernelList, image_size, kernel_size):
    error_rate_sum = 0
    for tuple in imageKernelList:
        image = tuple[0]
        kernel = tuple[1]
        pointsTuple = points

        error_rate_sum += test_points_variable(points,image,kernel,image_size,kernel_size)

    return error_rate_sum/ len(imageKernelList)

def find_error_rate(image_size,kernel_size,number_of_images, number_points_sets,points_per_set,input_size):
    dictionary = {}
    resultList = []
    point_coefficient = 0
    imageKernelList = generate_set(input_size,kernel_size,number_of_images) #  eg generating 4x4 images, 3x3 kernels, 10 images in the set
    for i in range(number_points_sets):
        # loop around this n times generating a new points set each loop to test
        upper = 1
        lower = -1
        pointsSet = sample_floats(lower,upper,image_size+kernel_size-2)
        #pointsSet = sample_floats_symmetric(lower,upper,image_size+kernel_size-2)
        average_error = test_points_for_image_list(pointsSet,imageKernelList,image_size,kernel_size)
        print("average_error :",average_error, "@ points :",pointsSet)
        dictionary[tuple(pointsSet)] = average_error
        point_coefficient += average_error

    # exit loop now we have all average error rates for the points sets we have tested
    sorted_d = sorted((value, key) for (key,value) in dictionary.items())

    point_coefficient = point_coefficient/input_size
    #print("Point coef: ",point_coefficient)
    return sorted_d

def refine_search(high_performance_points, number_of_images, image_size, kernel_size, points_per_set):
    refined = []
    for x in range(len(high_performance_points)):
        input_size = image_size + kernel_size -1
        imageKernelList = generate_set(input_size,kernel_size,number_of_images)
        points = high_performance_points[x][1]

        refined_error = test_points_for_image_list(points, imageKernelList, image_size, kernel_size)
        print("refined_error (average of ",number_of_images," runs) + points : ",(refined_error,points))
        refined.append((refined_error,points))

    return refined

""" one thing i notice with the results is that the best points always have a split on pos/negative numbers, never pos, pos, pos || neg, neg, neg"""

# testing get error rate
image_size = 4
kernel_size = 3
number_of_images = 100
number_points_sets = 1000
points_per_set = 5
input_size = image_size + kernel_size -1 # 4
st = time.time()
result = find_error_rate(image_size,kernel_size,number_of_images, number_points_sets,points_per_set,input_size)
end = time.time()
print("Time elapsed to do initial error rate calculations: ",end-st,"s")

print("Lowest error rate points (100) :")
i=0
for i in range(100):
    print(result[i])

high_performance_points = []
for x in range(100):
    high_performance_points.append(result[x])

number_images_refined = 1000
print("Refining top ",len(high_performance_points), " points with ",number_images_refined," runs each")

refined = refine_search(high_performance_points, number_images_refined, image_size, kernel_size, points_per_set)
refined_sorted = sorted(refined)
for x in refined_sorted:
    print("refined_average_error + points", x)

"""

# testing get error rate
image_size = 4
kernel_size = 3
number_of_images = 100
number_points_sets = 1000
points_per_set = 5
input_size = image_size + kernel_size -1 # 4
st = time.time()
result = find_error_rate(image_size,kernel_size,number_of_images, number_points_sets,points_per_set,input_size)
end = time.time()
print("Time elapsed to do initial error rate calculations: ",end-st,"s")

print("Lowest error rate points (100) :")
i=0
for i in range(100):
    print(result[i])

high_performance_points = []
for x in range(100):
    high_performance_points.append(result[x])

number_images_refined = 2000
print("Refining top ",len(high_performance_points), " points with ",number_images_refined," runs each")

refined = refine_search(high_performance_points, number_images_refined, image_size, kernel_size, points_per_set)
refined_sorted = sorted(refined)
for x in refined_sorted:
    print("refined_average_error + points", x)
"""

"""
# generates unique floating points random list
# possible improvements later to focus only on rational numbers and to try something with reciprocal values

points = sample_floats(0,1,3)
print(points)
"""

"""

# testing average error rate function for a given points set
# also returns a value to access the effectiveness of the point picker function
# it will return the average performance of the points sets picked
image_size = 2
kernel_size = 3
imageKernelList = generate_set(4,3,100) # 4x4 images, 3x3 kernels, 2 tuples total
points = [0.8095133296178773,0.10362463148985646,0.7774840747025125]
average_error = test_points_for_image_list(points,imageKernelList,image_size,kernel_size)
print(average_error)

"""

"""
#
# TESTING VARIOUS INPUT SIZED EG F(2,3)..
#

print("f(2,3)")
image_size = 2
kernel_size = 3
points = [10,20,30]
image = np.random.random((image_size+kernel_size-1,image_size+kernel_size-1))
kernel =  np.random.random((kernel_size,kernel_size))
error = test_points_variable(points,image,kernel,image_size,kernel_size)
print("Error: ",error)

print("f(2,3)")
image_size = 2
kernel_size = 3
points = [0,1,-1]
image = np.random.random((image_size+kernel_size-1,image_size+kernel_size-1))
kernel =  np.random.random((kernel_size,kernel_size))

error = test_points_variable(points,image,kernel,image_size,kernel_size)
print("Error: ",error)

print("f(4,3)")
image_size = 4
kernel_size = 3
points = [-1,-0.5,0,0.5,1] # number of points = (4+3)-2 = 5
image = np.random.random((image_size+kernel_size-1,image_size+kernel_size-1))
kernel =  np.random.random((kernel_size,kernel_size))

error = test_points_variable(points,image,kernel,image_size,kernel_size)
print("Error: ",error)

print("f(6,3)")
image_size = 6
kernel_size = 3
points = [-1.5,-1,-0.5,0,0.5,1,1.5] # number of points = (6+3)-2 = 7 points
image = np.random.random((image_size+kernel_size-1,image_size+kernel_size-1))
kernel =  np.random.random((kernel_size,kernel_size))

error = test_points_variable(points,image,kernel,image_size,kernel_size)
print("Error: ", error)

print("f(5,3)")
image_size = 5
kernel_size = 3
points = [-1.5,-1,-0.5,0,0.5,1] # number of points = (5+3)-2 = 6 points
image = np.random.random((image_size+kernel_size-1,image_size+kernel_size-1))
kernel =  np.random.random((kernel_size,kernel_size))

error = test_points_variable(points,image,kernel,image_size,kernel_size)
print("Error: ", error)

print("f(3,3)")
image_size = 3
kernel_size = 3
points = [-1,-0.5,0,0.5] # number of points = (3+3)-2 = 4 points
image = np.random.random((image_size+kernel_size-1,image_size+kernel_size-1))
kernel =  np.random.random((kernel_size,kernel_size))

error = test_points_variable(points,image,kernel,image_size,kernel_size)
print("Error: ", error)

print("f(2,2)")
image_size = 2
kernel_size = 2
points = [0.5,-0.5] # number of points = (2+2)-2 = 2 points
image = np.random.random((image_size+kernel_size-1,image_size+kernel_size-1))
kernel =  np.random.random((kernel_size,kernel_size))

error = test_points_variable(points,image,kernel,image_size,kernel_size)
print("Error: ", error)

"""


# Linear Algebra Take Home Quiz #3
# Author: Rocco Haro
# University of Alaska, Anchorage
# Oct '17'
# https://github.com/rocco-haro/Linear-Difference-Eq..git

import numpy as np

class StateMtx(object):
    """ Calculates the k+1 values for a linear difference equation
    """
    def __init__(self, *args):
        self.args = args
        # Converts input arr to matrix, which returns a N by 1 matrix.
        # So I take the transpose of this returned matrix to make it a valid
        # matrix for multiplication against the stateMtx
        self.initParameters = np.mat(self.args[0]).T

        # just hard codoing the matrix vals
        prep = [[0.97, 0.05, 0.10], [0.00, 0.90, 0.05], [0.03, 0.05, 0.85]]
        self.stateMtx = np.mat(prep) # converts array to matrix

    def printClass(self):
        print("Initial Parameters: ")
        print(self.initParameters)
        print("\nState Matrix: ")
        print(self.stateMtx)
        print("\n")

    def getNextK(self, currK):
        return self.stateMtx*currK

    def getKthValueAt(self, targetDay):
        currK = self.initParameters
        for x in range(targetDay):
            nextK = self.getNextK(currK)
            currK = nextK
        return currK

if __name__ == "__main__":
    k0 = [295, 55, 150 ]
    m = StateMtx(k0)
    m.printClass()
    res = m.getKthValueAt(30)
    print(res)