from math import acos, atan2, cos, sin, sqrt, pi, tan, atan, fmod
from typing import Sequence, Optional

import numpy as np
from numpy.linalg import norm
from scipy.optimize import root_scalar

from constants import z_hat, mu_earth, eps64

def limit_zero_2pi(angle):
    """
    Limit an angle such that it's between 0 and 2pi
    """
    return fmod(angle + 2 * np.pi, 2 * np.pi)

def cartesian_to_kepler(r: Sequence[float], v: Sequence[float], mu: float = mu_earth):
    """
    Convert Cartesian components to Kepler elements.

    Source:
        Wertz, James Richard. "Mission geometry; orbit and constellation
        design and management." Space Technology Library (2001).

    :param r: Position vector.
    :param v: Velocity vector.
    :param mu: Optional, Standard gravitational parameter, by defaults it's Earth's (3.98600441e14).
    :return: Tuple with the semi-major axis (a), eccentricity (e), inclination (i),
             right ascension of the ascending node (Ω), argument of periapsis (ω), true anomaly (θ).
             eccentric anomaly (E) and the mean anomaly (M).
    """
    # Make sure the arguments are numpy arrays.
    r = np.asarray(r)
    v = np.asarray(v)

    r_norm = norm(r)

    # Orbit angular momentum
    h = np.cross(r, v)
    h_hat = h / norm(h)

    # Ascending node
    n = np.cross(z_hat, h_hat)
    n_norm = norm(n)
    # In case of zero inclination there is no acending node and this
    # cross product will return a zero vector.
    # This is because this particular orientation results in a gibal lock
    # between the RAAN and argument of periapsis.
    # For this case we will define both these angles as zero.
    if n_norm > eps64:
        n_hat = n / n_norm
    else:
        n_hat = None

    # Eccentricity vector and eccentricity
    e_vec = np.cross(v, h) / mu - r / r_norm
    e = norm(e_vec)

    # Semi major axis
    a = 1 / (2 / r_norm - norm(v)**2/mu)

    # Inclination
    i = acos(h[2] / norm(h))

    # Right Ascension of the Ascending Node
    if n_hat is not None:
        raan = atan2(n[1], n[0])
        raan = limit_zero_2pi(raan)
    else:
        raan = 0

    # Argument of periapsis
    # There is no periapsis if the orbit is circular, so set it to zero.
    if n_hat is not None:
        e_hat = (e_vec / norm(e_vec))
        omega = acos(e_hat @ n_hat)
        if (np.cross(n_hat, e_vec) @ h) < 0:
            # omega is between pi and 2pi
            omega = 2 * pi - omega
        omega = limit_zero_2pi(omega)
    else:
        omega = 0

    # Anomalies
    true_anomaly = acos(r @ e_vec / (e * r_norm))
    if np.cross(e_vec, r) @ h < 0:
        # true_anomaly is between pi and 2pi
        true_anomaly = 2 * pi - true_anomaly
    true_anomaly = limit_zero_2pi(true_anomaly)

    eccentric_anomaly = atan2(sqrt(1 - e**2) * sin(true_anomaly), e + cos(true_anomaly))
    eccentric_anomaly = limit_zero_2pi(eccentric_anomaly)

    mean_anomaly = eccentric_anomaly - e * sin(eccentric_anomaly)
    mean_anomaly = limit_zero_2pi(mean_anomaly)

    return a, e, i, raan, omega, true_anomaly, eccentric_anomaly, mean_anomaly

def kepler_to_cartesian(a: float, e: float, i: float, raan: float, omega: float,
                        true_anomaly: Optional[float] = None,
                        eccentric_anomaly: Optional[float] = None,
                        mean_anomaly: Optional[float] = None,
                        mu: float = mu_earth):
    """
    Convert to Kepler elements to Cartesian components. At least one of the anomalies is required,
    the rest can be set to `None`.

    Source:
        Wertz, James Richard. "Mission geometry; orbit and constellation
        design and management." Space Technology Library (2001).

    :param a: Semi-major axis.
    :param e: Eccentricity.
    :param i: Inclination.
    :param raan: Right ascension of the ascending node (Omega).
    :param omega: Argument of periapsis.
    :param true_anomaly: True anomaly.
    :param eccentric_anomaly: Eccentric anomaly.
    :param mean_anomaly: Mean anomaly.
    :param mu: Optional, Standard gravitational parameter, by defaults it's Earth's (3.98600441e14).
    :return: Tuple with the cartesian position (r) and velocity (V).
    """

    # We need the true anomaly for the calculations, check if it was given.
    if true_anomaly is None:
        # If not we can calculate it from the eccentric anomaly, check if it was given.
        if eccentric_anomaly is None:
            # If not we can calculate the eccentric anomaly from the mean anomaly.
            # Check if it was given.
            if mean_anomaly is None:
                # We need at least one of the three anomalies. Raise error.
                raise ValueError('At least one of "True anomaly", "Eccentric anomaly" or "Mean anomaly" '
                                 'is required')
            else:
                eccentric_anomaly = eccentric_anomaly_from_mean_anomaly(e, mean_anomaly)

        # We should have a value for the eccentric anomaly at this point.
        true_anomaly = true_anomaly_from_eccentric_anomaly(e, eccentric_anomaly)

    # Semiparameter
    p = a * (1 - e**2)

    # Position in the perifocal coordinate system (pf)
    r_pf = np.zeros(3)
    r_pf[0] = p * cos(true_anomaly) / (1 + e * cos(true_anomaly))
    r_pf[1] = p * sin(true_anomaly) / (1 + e * cos(true_anomaly))

    # Velocity in the perifocal coordinate system
    v_pf = np.zeros(3)
    v_pf[0] = -sqrt(mu / p) * sin(true_anomaly)
    v_pf[1] = sqrt(mu / p) * (e + cos(true_anomaly))

    # Transformation matrix from the perifocal to inertial coordinate system.
    cr = cos(raan)
    co = cos(omega)
    ci = cos(i)
    sr = sin(raan)
    so = sin(omega)
    si = sin(i)

    c_pf = np.array([[cr*co - sr*so*ci, -cr*so - sr*co*ci, sr*si],
                     [sr*co + cr*so*ci, -sr*so + cr*co*ci, -cr*si],
                     [so*si, co*si, ci]])

    # Position and velocity in the inertial frame
    position = c_pf @ r_pf
    velocity = c_pf @ v_pf

    return position, velocity

def eccentric_anomaly_from_mean_anomaly(e: float, mean_anomaly: float):
    """
    Calculates the eccentric anomaly from the mean anomaly using a
    numerical root-finding method.

    :param e: Eccentricity.
    :param mean_anomaly: Mean anomaly.
    :return: Mean anomaly
    """
    # Equation to solve. This is the equation to calculate the mean
    # anomaly from the eccentric anomaly minus the mean anomaly.
    # So the solution will be at the root.
    def f(e_ano):
        return e_ano - e * sin(e_ano) - mean_anomaly

    # Solve it numerically, we're letting scipy choose a method for us here.
    solution = root_scalar(f, x0=mean_anomaly, bracket=[0, 2 * pi])
    return solution.root

def true_anomaly_from_eccentric_anomaly(e: float, eccentric_anomaly):
    """
    Calculates the true anomaly from the eccentric anomaly

    :param e: Eccentricity.
    :param eccentric_anomaly: Eccentric anomaly.
    :return: True anomaly
    """
    numerator = sqrt(1+e) * sin(eccentric_anomaly/2)
    denominator = sqrt(1-e) * cos(eccentric_anomaly/2)
    return 2 * atan2(numerator, denominator)

#     return 2 * atan(sqrt((1+e)/(1-e)) * tan(eccentric_anomaly / 2))


#Hypothesis Testing - ability to ask and quantitatively answer using data(prob.)
#If you formulate two hypothesis how can you identify the true one
#eg: finding whether a player in game has cheated in die roll
#simple QUESTION -> is tommy rolling too many 6?
#How to find out:
#1) Visualise the data
#2) Reduce or quantify the data
#3) Pose our hypothesis(or visualize)
#4) Calculate

import numpy as np
import matplotlib.pyplot as plt

data = np.loadtxt("loaded_500.txt")

unique,counts = np.unique(data,return_counts=True)
print(unique,counts)
plt.hist(data)
plt.show()
#98 times 6s rolled
#plotting done
num_sixes = (data==6).sum()
num_total = data.size

#Pose Hypothesis
#We have two outcomes, if we have a fair die,when we roll it the probability of
#getting 6  = (1/6). As this is discrete with 2 option,we go for binomial dist.

#we also want to know what is the probability of rolling 6 98 times (or more)

from scipy.stats import binom
n = np.arange(num_total)
prob_n = binom.pmf(n,num_total,1/6)
plt.plot(n,prob_n,label="Prob num")
plt.legend()
plt.show()
#now we know there's some problem

prob_n = binom.pmf(n,num_total,1/6)
plt.plot(n,prob_n,label="Prob num")
plt.axvline(num_total/6,ls="--",lw=1,label="Mean num")
plt.axvline(num_sixes,ls=":",color="#ff7272",label="Obs num")
plt.xlabel("Num sizes rolled out of %d rolls".format(num_total))
plt.ylabel("Probability")
plt.legend()
plt.show()
#can't say much from this plot as the observed and mean number are close
#we want the green area under the curve from 98 to 500
#we can do this using survival function OR

d = binom(num_total,1/6)#d is a binomial distribution configured to probability
#and to num_total
plt.plot(n,d.sf(n))
plt.show()

plt.plot(n,d.sf(n))
plt.axvline(num_sixes,ls="--")
sf = d.sf(num_sixes)
plt.axhline(sf,ls="--")
plt.xlabel("num of sixes")
plt.ylabel("Survival function")
plt.show()
print(f"Only {sf * 100:0.1f}% of the time fair die will roll this many or more sixes")
#You would roll more than this many sixes
# CDF <= k ; so SF > k and not >= k
#from this we can see that person is a liar as we can say that 96.3%, his die is
#loaded


from sympy import *
import sympy
from math import pi,e
from math import pow
import numpy as np

# variables
x = Symbol('x')
y = Symbol('y')
z = Symbol('z')

# model
G = sympy.Matrix([3*x-cos(y*z)-3./2.,
                  4.*x**2-625.*y**2+2.*y-1.,
                  sympy.exp(-x*y)+20.*z+(10.*pi-3.)/3.])

# Objective function
F     = G.T*G/2

# Objective functio gradient
gradF = G.jacobian([x,y,z]).T*G

# Step size
gamma = 0.001
precision = 0.00001
objective_function_eval = 1
# Max iteration number
max_iters = 190
iters = 0

# Initial location
cur_x=np.matrix([0.,0.,0.])
prev_x=np.matrix([0.,0.,0.])

while ( objective_function_eval < 100000    ) & \
      ( objective_function_eval > precision ) & \
      ( iters < max_iters ):

    prev_x[:] = cur_x[:]

    # Gradient of the model:
    dF=gamma * gradF.subs([(x,prev_x[0,0]),(y,prev_x[0,1]),(z,prev_x[0,2])])

    # Place the next point in the gradient descent direction
    for i,val in enumerate(dF):
        cur_x[0,i] -= val

    # Evaluaton the objective function at this value
    objective_function_eval = (F.subs([ (x,cur_x[0,0]), (y,cur_x[0,1]), (z,cur_x[0,2]) ]))[0,0]

    print('-----------------')
    print('Iteration: ' + str(iters))
    print('dF(x1): '    + str(dF))
    print('x0:     '    + str(prev_x))
    print('x1:     '    + str(cur_x))
    print('Objective function evaluation: ' + str(objective_function_eval))

    iters+=1


#Import the necessary Libraries
import matplotlib.pyplot as plt
import pandas as pd
import numpy as np
from sklearn.metrics import mean_squared_error
from sklearn.metrics import r2_score
from sklearn.linear_model import LinearRegression
from sklearn.model_selection import train_test_split
from sklearn.neighbors import KNeighborsRegressor
#%matplotlib inline

#Read The Data Set
df=pd.read_csv('Salary_Data.csv')
#View your data
df.head(5)

#Draw a scatter Plot to see relationship between the variables
x=df[['YearsExperience']] # predictor
y=df['Salary'] #response or output variable
plt.scatter(x,y)
plt.xlabel('Years of Experience' ,fontsize=20)
plt.ylabel('Salary',fontsize=20)
plt.title('Scatter Plot')

#Split Data set
x_train,x_test,y_train,y_test=train_test_split(x,y,train_size=0.7)

#Model Developement
lr=LinearRegression()
model=lr.fit(x_train,y_train)
print('The Coefficients for this model is ',model.coef_)
y_train_pred=model.predict(x_train)
mse_train=mean_squared_error(y_train,y_train_pred)
print(f"MSE for Train Data set is {mse_train}")
y_test_pred=model.predict(x_test)
mse_test=mean_squared_error(y_test,y_test_pred)
print(f"MSE for Train Data set is {mse_test}")

#Plot your regression result
fig,ax=plt.subplots(figsize=(8,8))
ax.scatter(x,y,label='Original Data')
plt.plot(x_test,y_test_pred,color='red',label='Predictions')
plt.xlabel('Years of Experience' ,fontsize=20)
plt.ylabel('Salary',fontsize=20)
plt.title('Simple Linear Regression',fontsize=22)
ax.legend()


#Program Written by KOUSHAL K H.
import numpy as np
def forward_propagation(X, W1, b1, W2, b2):
    Z1 = np.dot(W1, X)+b1
    A1 = np.tanh(Z1)
    Z2 = np.dot(W2, A1)+b2
    A2 = 1/(1+np.exp(-Z2))  # Sigmoid
    return A1, A2

def nn_model(X, Y, n_h, num_iterations=250, lr=1.2):
    n_x, n_y, m = X.shape[0], Y.shape[0], Y.shape[1]
    W1 = np.random.randn(n_h, n_x) * 0.01
    b1 = np.zeros((n_h, 1))
    W2 = np.random.randn(n_y, n_h) * 0.01
    b2 = np.zeros((n_y, 1))
    for i in range(0, num_iterations):
        #Forward Pass
        A1, A2 = forward_propagation(X, W1, b1, W2, b2)
        # LOSS Y*log(A2) + (1-Y)*log(1-A2)
        logprobs = np.multiply(np.log(A2), Y)+np.multiply(np.log(1-A2), (1-Y))
        cost = (-1/m)*np.sum(logprobs)
        cost = np.squeeze(cost)  # To make sure dimentions are proper
        #BackwardProp
        dZ2 = A2-Y
        dW2 = (1/m)*np.dot(dZ2, A1.T)
        db2 = (1/m)*np.sum(dZ2, axis=1, keepdims=True)
        dZ1 = np.multiply(np.dot(W2.T, dZ2), (1 - np.power(A1, 2)))
        dW1 = (1/m)*np.dot(dZ1, X.T)
        db1 = (1/m)*np.sum(dZ1, axis=1, keepdims=True)
        #Update parameters
        W1 = W1 - lr*dW1
        b1 = b1 - lr*db1
        W2 = W2 - lr*dW2
        b2 = b2 - lr*db2
        print("Cost after iteration %i: %f" % (i, cost))
    return W1, b1, W2, b2

def predict(X, W1, b1, W2, b2):
    A2 = forward_propagation(X, W1, b1, W2, b2)
    predictions = A2[1] > 0.5
    return predictions

#Generating input for NN can replace next 4 lines with any input of your own
X = np.random.randn(2, 400)*15
Y = np.array([(1 if np.sum(a) > 0 else 0) for a in np.rollaxis(X, 1)])
Y[:70] = 1  # Adding Noice. Remove this line or decrease 70 for more accuracy
Y = Y.reshape(1, 400)
n_x, n_h, n_y = 2, 4, 1
W1, b1, W2, b2 = nn_model(X, Y, n_h)  # Train the model
predictions = predict(X, W1, b1, W2, b2)
print("ACUURACY={}%".format(np.sum((predictions == Y), axis=1)/float(Y.size)*100))
"""
NOTE:
If no of layers is more than 2:
    copy and paste the lines of 2nd layer and make it 3
To increase the accuracy:
    increase num_iterations
If any other input data is used:
    Accuracy may differ.
"""


import os
import random
import math
import sys
from Crypto.PublicKey import RSA
from sympy import *

sys.setrecursionlimit(2000)

####Prime Functions####
def millerRabin(m, k):
    """
    Miller Rabin pseudo-prime test
    return True means likely a prime, (how sure about that, depending on k)
    return False means definitely a composite.
    """
    s=1
    d = (m-1)/2
    while d%2 == 0:
        d /= 2
        s += 1
    for r in range(0,k):
        rand_num = random.randint(1,m-1)
        y = pow(rand_num, d, m)
        prime = False
        if (y == 1):
            prime = True
        for i in range(0,s):
            if (y == m-1):
                prime = True
                break
            else:
                y = (y*y)%m
        if not prime:
            return False
    return True

def findAPrime(a, b, k):
    """Return a pseudo prime number roughly between a and b,
    (could be larger than b). Raise ValueError if cannot find a
    pseudo prime after 10 * ln(x) + 3 tries. """
    x = random.randint(a, b)
    for i in range(0, int(10 * math.log(x) + 3)):
        if millerRabin(x, k):
            return x
        else:
            x += 1

    raise ValueError

####Modular Arithmetics####
def gcd(x, y):
    """returns the Greatest Common Divisor of x and y"""
    while y != 0:
        (x, y) = (y, x % y)
    return x

def egcd(a, b):
    """return a tuple of three values: x, y and z, such that x is
    the GCD of a and b, and x = y * a + z * b"""
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)

def modInv(a, m):
    """returns the multiplicative inverse of a in modulo m as a
    positive value between zero and m-1"""
    g, x, y = egcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m

def modExp(a, d, n):
    """returns a ** d (mod n)"""
    assert d >= 0
    assert n >= 0
    base2D = int2baseTwo(d)
    base2DLength = len(base2D)
    modArray = []
    result = 1
    for i in range(1, base2DLength + 1):
        if i == 1:
            modArray.append(a % n)
        else:
            modArray.append((modArray[i - 2] ** 2) % n)
    for i in range(0, base2DLength):
        if base2D[i] == 1:
            result *= base2D[i] * modArray[i]
    return result % n

def findInvPow(x,n):
    """Finds the integer component of the n'th root of x,
    an integer such that y ** n <= x < (y + 1) ** n.
    """
    high = 1
    while high ** n < x:
        high *= 2
    low = high/2
    while low < high:
        mid = (low + high) // 2
        if low < mid and mid**n < x:
            low = mid
        elif high > mid and mid**n > x:
            high = mid
        else:
            return mid
    return mid + 1

def int2baseTwo(x):
    """x is a positive integer. Convert it to base two as a list of integers
    in reverse order as a list."""
    assert x >= 0
    bitInverse = []
    while x != 0:
        bitInverse.append(x & 1)
        x >>= 1
    return bitInverse

####RSA Functions#####
class EmbeddedRSA:
    def __init__(self, rsaKey=None):
        if rsaKey == None:
            rsaKey = self._newKey(2 ** 1023, (2 ** 1024) - 1, 100)
        self.key = rsaKey

    def _newKey(self, a, b, k):
        try:
            p = findAPrime(a, b, k)
            while True:
                q = findAPrime(a, b, k)
                if q != p:
                    break
        except:
            raise ValueError
        n = p * q
        m = (p - 1) * (q - 1)
        while True:
            # Optimize for decryption speed
            limit = findInvPow(n, 4) >> 16
            d = random.randint(1, limit)
            if gcd(d, m) == 1:
                break
        e = modInv(d, m)
        return {'n': n, 'e':e, 'd':d}

    def Encrypt(self, msg):
        msgNum = int(msg.encode('hex'), 16)
        msgCrypt = modExp(msgNum, self.key['e'], self.key['n'])
        return msgCrypt

    def Decrypt(self, num):
        msgNum = modExp(num, self.key['d'], self.key['n'])
        msg = ''.join([chr(int(hex(msgNum)[i:i+2], 16)) for i in range(2,len(hex(msgNum))-1,2)])
        return msg

    def GetKey(self):
        return self.key

####MY FUNCTIONS####
def ContFracConv(num, denom):
  frac = []
  numArray = []
  denomArray = []

  q = num // denom
  r = num % denom
  frac.append(q)

  while r != 0:
    num, denom = denom, r
    q = num // denom
    r = num % denom
    frac.append(q)

  for i in range(len(frac)):
    if i == 0: # First Fraction
      numi = frac[i]
      denomi = 1
    elif i == 1: # Second Fraction
      numi = frac[i]*frac[i-1] + 1
      denomi = frac[i]
    else: # All other Fractions
      numi = frac[i]*numArray[i-1] + numArray[i-2]
      denomi = frac[i]*denomArray[i-1] + denomArray[i-2]

    numArray.append(numi)
    denomArray.append(denomi)
    yield (numi, denomi)

def phiN(e, d, k):
  return ((e * d) - 1)//k

def wienerAttack(conv):
  for k, d in  conv:
    if k == 0:
      continue

    # Quadratic Equation is :
    # x^2 - phi*x + n = 0
    phi = phiN(e, d, k)

    x = Symbol('x', integer=True)
    roots = solve(x**2 + (phi - n - 1)*x + n, x)

    if len(roots) == 2:
      pp, pq = roots
      # pp = possible p
      # pq = possible q
      if pp*pq == n:
        if modInv(d, (pp - 1) * (pq - 1)) == e:
          rsa = EmbeddedRSA({'n': n, 'e':e, 'd':d})
          plaintext = rsa.Decrypt(ciphertext)
          return plaintext

if __name__ == "__main__":
  # Read flag.crypt
  # First line is the Public Encryption Key - E
  # Second line is the Modulous - N
  # Third line is the CipherText
  file = open('flag.crypt', 'r')
  lines = file.readlines()

  # Assign values to the lines read out from the file
  e = int(lines[0])
  n = int(lines[1])
  ciphertext = int(lines[2])

  # Find the Convergents and then run WeinersAttack
  print("Your decrypted message is :\n",wienerAttack(ContFracConv(e,n)))


from sympy import symbols
from sympy import Matrix
from sympy import diff
from sympy import linsolve

x, y = symbols('x y')
f1 = x**2 + y**2 - 2.1
f2 = (x**2) / 2 - y**2 - 0.15269
eps = 0.01

def getVectorNorm(v):
    max = abs(v[0])
    for x in v:
        max = abs(x) if abs(x) > max else max
    return max

_J = Matrix([[diff(f1, x), diff(f1, y)], [diff(f2, x), diff(f2, y)]])

def J(x1, x2):
    return _J.subs([(x, x1), (y, x2)])

def F(x1, x2):
    return (Matrix)([f1.subs([(x, x1), (y, x2)]), f2.subs([(x, x1), (y, x2)])])

# Обычный метод Ньютона
count = 0
accuracy = eps + 1
x0 = [1, 1]
while accuracy > eps and count < 50:
    sigmaX = linsolve((J(x0[0], x0[1]), -F(x0[0], x0[1])))
    x1 = [sigmaX.args[0][0] + x0[0], sigmaX.args[0][1] + x0[1]]
    accuracy = getVectorNorm(sigmaX.args[0])
    x0 = x1
    count += 1

print(x0)

# Модифицированный метод Ньютона
count = 0
accuracy = eps + 1
x0 = [1, 1]
x00 = x0
while accuracy > eps and count < 50:
    sigmaX = linsolve((J(x00[0], x00[1]), -F(x0[0], x0[1])))
    x1 = [sigmaX.args[0][0] + x0[0], sigmaX.args[0][1] + x0[1]]
    accuracy = getVectorNorm([x1[0] - x0[0], x1[1] - x0[1]])
    x0 = x1
    count += 1

print(x0)

# Разностный метод Ньютона
h = 0.5

def A(x1, x2):
    return (Matrix)([[(f1.subs([(x, x1 + h), (y, x2)]) - f1.subs([(x, x1), (y, x2)])) / h, (f1.subs([(x, x1 + h), (y, x2)]) - f1.subs([(x, x1), (y, x2)])) / h],
                     [(f2.subs([(x, x1 + h), (y, x2)]) - f2.subs([(x, x1), (y, x2)])) / h, (f2.subs([(x, x1 + h), (y, x2)]) - f2.subs([(x, x1), (y, x2)])) / h]])

count = 0
accuracy = eps + 1
x0 = [1, 1]
while accuracy > eps and count < 50:
    sigmaX = linsolve((A(x0[0], x0[1]), -F(x0[0], x0[1])))
    x1 = [sigmaX.args[0][0] + x0[0], sigmaX.args[0][1] + x0[1]]
    accuracy = getVectorNorm([x1[0] - x0[0], x1[1] - x0[1]])
    x0 = x1
    count += 1

print(x0)


from pwn import *
from Crypto.Util.number import *

debug = False
r = remote("crypto.chall.pwnoh.io", 13376, level = 'debug' if debug else None)

payload = 1

r.recvuntil('n = ')
n = int(r.recvline().decode())

#Pollard's p-1 factorisation algorithm
def factor(n):
    a = 2
    b = 2
    while True:
        if b % 10000 == 0:
            pass

        a = pow(a, b, n)

        p = GCD(a - 1, n)
        if 1 < p < n:
            print("FOUND prime factor")
            return p

        b += 1

p = factor(n)
q = n // p
assert n == p * q

#Please calculate (59 ** 59 ** 59 ** 59 ** 1333337) % n)

#https://math.stackexchange.com/questions/3558102/how-to-compute-333-phantom-bmod-46-for-pow/3559055
from sympy.ntheory import totient

assert GCD(59, n) == 1

phi = (p - 1) * (q - 1)
secondPhi = totient(phi)
thirdPhi = totient(secondPhi)
fourthPhi = totient(thirdPhi)

answer = pow(59, (pow(59, (pow(59, (pow(59, 1333337 % fourthPhi, thirdPhi)), secondPhi)), phi)), n)
r.sendlineafter('>>> ', str(answer))

print(r.recvall())
#b"WTF do you own a supercomputer? Here's your flag:\nbuckeye{phee_phi_pho_phum_v3ry_stup1d_puzzle}\n"


from math import cos, sin
from numpy import array, eye
from numpy.linalg import matrix_power, norm

def skew(k):
    return array([[0.0, -k[2], k[1]], [k[2], 0.0, -k[0]], [-k[1], k[0], 0.0]])

def normalize(k):
    return k / norm(k)

def rodrigues(k, theta):
    """
    Rodrigues rotation formula

    :param k: Axis of rotation
    :param theta: Angle of ratation
    :return: Rotation matrix associated with (v, theta)
    """

    k_normalized = normalize(k)

    return eye(3) + sin(theta) * skew(k_normalized) + (1 - cos(theta)) \
           * matrix_power(skew(k_normalized), 2)


import numpy as np

def l2_norm(x: np.ndarray, y: np.ndarray) -> np.float64:
    """calculates L2 norm.

    Parameters
    ----------
    x : np.ndarray
        vector.(1-dim numpy ndarray)
    y : np.ndarray
        vector.(1-dim numpy ndarray)

    Returns
    -------
    np.float64
        L2 norm of x and y.
    """
    norm = np.linalg.norm(x - y)
    return norm


import numpy as np
from scipy.integrate import quad

def payoff(payoff, S,K):
    """
    Payoff function for European options
    """
    if payoff == "call":
        return np.maximum( S - K, 0 )
    elif payoff == "put":
        return np.maximum( K - S, 0 )

def fourierPricer(K,S0,payoff,r,T,cf,xmax=np.inf,limit=1000):

        """
        Lewis integral for European options
        K = strike
        S0 = initial value
        payoff = call/put
        cf = characteristic function
        """
        k = np.log(S0/K)+r*T

        integrand = lambda u: np.real( np.exp(u*k*1j) * cf(u - 0.5j) ) * 1/(u**2 + 0.25)

        call = S0 - np.sqrt(S0 * K) * np.exp(-(r)*T)/np.pi * \
               quad(integrand, 0, xmax, limit=limit)[0]

        if payoff=="call":
            return call

        if payoff=="put":
            return call - S0 + K*np.exp(-(r)*T) # Call-put parity


"""
    Given the real and imaginary parts of a complex number,
    determine if it is a candidate for membership in the Mandelbrot
    set given a fixed number of iterations.

    PSEUDO CODE - Wikipedia

    for each pixel (Px, Py) on the screen do
    x0 = scaled x coordinate of pixel (scaled to lie in the Mandelbrot X scale (-2.5, 1))
    y0 = scaled y coordinate of pixel (scaled to lie in the Mandelbrot Y scale (-1, 1))
    x := 0.0
    y := 0.0
    iteration := 0
    max_iteration := 1000
    while (x*x + y*y ≤ 2*2 AND iteration < max_iteration) do
        xtemp := x*x - y*y + x0
        y := 2*x*y + y0
        x := xtemp
        iteration := iteration + 1

    color := palette[iteration]
    plot(Px, Py, color)

    Author: Harivinay V
    github: https://github.com/M87K452b

"""
import numpy as np
import matplotlib.pyplot as plt
from numba import jit
from timeit import default_timer as timer

N = 250
r = 4
nx = 2 * 1920
ny = 2 * 1080
xmin = -2.5  # -0.7721582 #Ocean#-0.7318744 #Snowflake#-0.37465401 #Default#-0.05
xmax = 1.0  # -0.7701566 #Ocean#-0.7318878 #Snowflake#-0.37332411 #Default##-0.55
ymin = -1.0  # 0.1143422 #Ocean#0.1845309 #Snowflake#0.659227668 #Default##0.5
ymax = 1.0  # 0.1163601 #Ocean#0.1845413 #Snowflake#0.66020767 #Default##1.0
x = np.linspace(xmin, xmax, nx)
y = np.linspace(ymin, ymax, ny)
fractal = np.zeros([nx, ny])

@jit(nopython=True)
def mandelbrot(x0, y0, N, r):
    xold = 0
    yold = 0
    z = 0
    rnew = x0**2 + y0**2
    while rnew < r and z < N:
        xnew = xold**2 - yold**2 + x0
        ynew = 2 * xold * yold + y0

        xold = xnew
        yold = ynew

        rnew = xnew**2 + ynew**2

        z += 1
    return z

@jit(nopython=True)
def fractal_plot(xmin, xmax, ymin, ymax, fractal, N, r):
    for i in range(nx):
        for j in range(ny):
            z = mandelbrot(x[i], y[j], N, r)
            fractal[i, j] = z

start = timer()
fractal_plot(xmin, xmax, ymin, ymax, fractal, N, r)
compute_time = timer() - start

print("Mandelbrot computed in %f s" % compute_time)
f = plt.figure(figsize=(12, 16))
plt.imshow(fractal.T, cmap='viridis')
plt.axis('off')
plt.show()

f.savefig("galaxy_spiral.png", bbox_inches='tight', dpi=800)


# -*- coding: utf-8 -*-
"""
Created on Fri Jan 10 16:23:23 2020

@author: Dr. Dr. Danny E. P. Vanpoucke
@web   : https://dannyvanpoucke.be
"""
import pandas as pd
import numpy as np

def Lin1DFunc(x:list, intercept: float, slope: float)-> list:
    """
    Create linear data: y=slope*x + intercept
    """
    return list(np.dot(slope,xi) + intercept for xi in x)

def LinNDFunc(x:list, intercept: float, slope: list)-> list:
    """
    Create linear data: y=slope1*x1+slope2*x2+...+slopeN*xN + intercept
    """
    return list(np.dot(slope,xi) + intercept for xi in x)

def LinPolyNDFunc(x:list, intercept: float, slope: list)-> list:
    """
    Create linear Polynomial data: y=slope1*x^1+slope2*x^2+...+slopeN*x^N + intercept
    """
    npow=[]
    for i in range(len(slope)):
        npow.append(i+1)

    features=[]
    for xi in x:
        features.append(np.power(xi,npow))
#    print("=============================")
#    print("slope=",slope)
#    print("x    =",x)
#    print("xi   =",features)
#    print("intercept=",intercept)
#    print("dot=",list(np.dot(slope,xi) for xi in features))
#    print("=============================")
    return list(np.dot(slope,xi) + intercept for xi in features)

def Exp1DFunc(x:list, intercept: float, slope: float)-> list:
    """
    Create a 1D exponential dataset: exp(slope*x) - intercept
    """
    return list(np.exp(slope*xi)-intercept for xi in x)

def SinFunc(x:list, intercept: float, slope: float)-> list:
    """
    Create a 1D sine dataset: sin(slope*x) + intercept
    """
    return list(np.sin(slope*xi)+intercept for xi in x)

def Create1DData(Func1D, length:int, intercept: float=0.0, coefficient: float=1.0,
             lbound: float=0.0, ubound: float=1.0,
             dist:str = None, dwidth: float=0.2) -> pd.DataFrame:
    """
    Create a noisy 1D dataset using a specific function.

    parameters:
        - length : size of the dataset
        - intercept : the intercept parameter of the function
        - coefficient: the coefficient (slope, factor,...) of the function
        - lbound and ubound: lower and uper bound of the x-range.
        - dist: Type of distribution. String = 'normal' or 'uniform' DEFAULT=normal
        - dwidth : the width of the distribution, Default=0.2
        - Func1D : 1-D function to generate data with. This function returns a list of results.
    """
    #create two lists of the correct length, filled with linear related data
    if (dist==None):
        dist='normal'
    wd=ubound-lbound
    x=lbound+np.random.random_sample(size=length)*wd
    y=list()

    if (dist=='normal'):
        rdn=(np.random.normal(size=length))*dwidth #gaussian  distribution with mean=0, and stdev=1
    elif (dist=='uniform'):
        rdn=(np.random.uniform(size=length)-1.0)*dwidth #uniform distribution from 0..1
    else:
        rdn=(np.random.normal(size=length))*dwidth #gaussian  distribution with mean=0, and stdev=1

    y=Func1D(x,intercept,coefficient)+rdn

    #transform in list of tuples
    xy_tuples=list(zip(x,y))
    #and convert into dataframe
    df=pd.DataFrame(xy_tuples, columns = ['Feat_x','Target_y'])

    return df

#def CreateExponential1D(length:int, intercept: float=0.0, scaleX: float=1.0, lbound: float=0.0, ubound: float=1.0) -> pd.DataFrame:
#    """
#    Create a (gaussian) noisy 1D exponential dataset: exp(slope*x) - intercept
#
#    parameters:
#        - length : size of the dataset
#    """
#    #create two lists of the correct length, filled with linear related data
#    wd=ubound-lbound
#    x=lbound+np.random.random_sample(size=length)*wd
#    y=list()
#
#    rdn=(np.random.normal(size=length))*0.2 #gaussian  distribution with mean=0, and stdev=1
#    y=Exp1DFunc(x,intercept,scaleX)+rdn
#
#    #transform in list of tuples
#    xy_tuples=list(zip(x,y))
#    #and convert into dataframe
#    df=pd.DataFrame(xy_tuples, columns = ['Feat_x','Target_y'])
#
#    return df

#def CreateSin1D(length:int, intercept: float=0.0, scaleX: float=1.0, lbound: float=0.0, ubound: float=1.0) -> pd.DataFrame:
#    """
#    Create a (gaussian) noisy 1D sine dataset: sin(slope*x) + intercept
#
#    parameters:
#        - length : size of the dataset
#    """
#    #create two lists of the correct length, filled with linear related data
#    wd=ubound-lbound
#    x=lbound+np.random.random_sample(size=length)*wd
#    y=list()
#
#    rdn=(np.random.normal(size=length))*0.05 #gaussian  distribution with mean=0, and stdev=1
#    y=SinFunc(x,intercept,scaleX)+rdn
#
#    #transform in list of tuples
#    xy_tuples=list(zip(x,y))
#    #and convert into dataframe
#    df=pd.DataFrame(xy_tuples, columns = ['Feat_x','Target_y'])
#
#    return df

def Lin3DFunc(x:list, intercept: float, slope: list)-> list:
    return slope[0]*x[0,:]+slope[1]*x[1,:]+slope[2]*x[2,:]+intercept

def CreateLinear3D(length:int, intercept: float=0.0, slope: list=[1.0,1.0,1.0], lbound: float=0.0, ubound: float=1.0) -> pd.DataFrame:
    """
    Create a noisy 3D linear dataset

    parameters:
        - length : size of the dataset
    """
    #create two lists of the correct length, filled with linear related data
    wd=ubound-lbound
    x1=lbound+np.random.random_sample(size=length)*wd
    x2=lbound+np.random.random_sample(size=length)*wd
    x3=lbound+np.random.random_sample(size=length)*wd
    x=list()
    x.append(x1)
    x.append(x2)
    x.append(x3)
    y=list()
    rdn=(np.random.normal(size=length))*0.2 #gaussian  distribution with mean=0, and stdev=1
    y=Lin3DFunc(x,intercept,slope)+rdn

    #transform in list of tuples
    xy_tuples=list(zip(x1,x2,x3,y))
    #and convert into dataframe
    df=pd.DataFrame(xy_tuples, columns = ['Feat_x1','Feat_x2','Feat_x3','Target_y'])

    return df


# -*- coding: utf-8 -*-
#/usr/bin/env python
"""
chebpy.test
===========

"""

from time import time
import numpy as np
from numpy.fft import fft, ifft
import matplotlib.pyplot as plt

from chebpy import barycentric_weights, barycentric_weights_cg
from chebpy import barycentric_weights_cgl, barycentric_matrix
from chebpy import interpolation_point, cheb_interpolation_point
from chebpy import interpolation_1d, interpolation_2d
from chebpy import cheb_interpolation_1d, cheb_interpolation_2d
from chebpy import almost_equal

def test_barycentric_weights():
    N = 16
    ii = np.arange(N+1)
    print 'Chebyshev Gauss-Lobatto points'
    xx = np.cos(ii * np.pi / N)
    w = barycentric_weights(xx)
    w1 = barycentric_weights_cgl(N)
    print w / np.max(w)
    print w1
    print w/np.max(w) - w1
    print almost_equal(w/np.max(w),w1)

    print 'Chebyshev Gauss points'
    xx = np.cos((2*ii+1)*np.pi/(2*N+2))
    w = barycentric_weights(xx)
    w1 = barycentric_weights_cg(N)
    print w / np.max(w)
    print w1
    print w/np.max(w) - w1
    print almost_equal(w/np.max(w),w1)

def test_interpolation_1d():
    '''
    Test function
        f(x) = |x| + x/2 - x^2
    '''

    N = 64
    M = 1000

    ii = np.arange(N+1)
    xx = np.cos(ii * np.pi / N)
    w = barycentric_weights_cgl(N)
    f = np.abs(xx) + .5 * xx - xx**2
    plt.plot(xx,f,'.')

    kk = np.arange(M+1)
    yy = (2. / M) * kk - 1.
    f1 = np.zeros_like(yy)
    f2 = np.zeros_like(yy)
    f3 = np.zeros_like(yy)
    f4 = np.zeros_like(yy)

    print 'Interpolation by point-by-point'
    t = time()
    for j in xrange(M+1):
        f1[j] = cheb_interpolation_point(yy[j],f)
        f2[j] = interpolation_point(yy[j], f, xx, w)
    print 'point-by-point time: ', time() - t
    print f2-f1
    print almost_equal(f1,f2)
    plt.plot(yy, f1)

    print 'Interpolation by matrix'
    t = time()
    f3 = cheb_interpolation_1d(yy, f)
    f4 = interpolation_1d(yy, f, xx, w)
    print 'matrix time: ', time() - t
    print f3-f1
    print f4-f1
    print almost_equal(f3,f1), almost_equal(f4,f1)
    plt.plot(yy, f3, 'r')

    plt.show()

def test_interpolation_2d():
    '''
    Test function
        f(x,y) = x^2 + y^2
        f(x,y) = exp(-(x^2+y^2))
    '''

    Nx = 32
    Ny = 32
    Mx = 100
    My = 200

    ix = np.arange(Nx+1)
    x = np.cos(ix * np.pi / Nx)
    wx = barycentric_weights_cgl(Nx)
    iy = np.arange(Ny+1)
    y = np.cos(iy * np.pi / Ny)
    wy = barycentric_weights_cgl(Ny)
    X, Y = np.meshgrid(x, y)
    #f = X**2 + Y**2
    f = np.exp(-(X**2 + Y**2))
    plt.plot(X[Ny/2,:], f[Ny/2.,:],'.')

    kx = np.arange(Mx+1)
    u = (2. / Mx) * kx - 1.
    ky = np.arange(My+1)
    v = (2. / My) * ky - 1.
    U, V = np.meshgrid(u, v)
    #f0 = U**2 + V**2
    f0 = np.exp(-(U**2 + V**2))

    f1 = np.zeros_like(f)
    f2 = np.zeros_like(f)

    print 'cheb interpolation'
    t = time()
    f1 = cheb_interpolation_2d(u, v, f)
    print 'cheb time: ', time() - t

    print 'general interpolation'
    t = time()
    f2 = interpolation_2d(u, v, f, x, y, wx, wy)
    print 'general time: ', time() - t

    print f2-f1
    print 'Is general and cheb interpolation the same? ',
    print almost_equal(f1,f2)
    print 'Interpolation error: ', np.linalg.norm(f1-f0)
    plt.plot(U[My/2,:], f1[My/2.,:])

    plt.show()

if __name__ == '__main__':
    #test_barycentric_weights()
    #test_interpolation_1d()
    test_interpolation_2d()


#!/usr/bin/env python
"""
Smoothing by regularization of data using constraints on the smooth
solution.
"""
from __future__ import print_function

import numpy as np
from matplotlib.pyplot import *
from scikits import datasmooth as ds

# create simulated data
npts = 100
xmin = 0
xspan = 1
x = np.linspace(xmin,xmin+xspan,npts)
# give variability to x if desired
x = x + xspan/npts*(np.random.rand(npts)-0.5)
x[0] = xmin # force x[1] to be the defined xmin

yt = 4*x/(1+4*x)
ytp = 4/(4*x+1)**2
yt2p = -32/(4*x+1)**3
stdev = 5e-2*np.max(yt)
y = yt + stdev*np.random.randn(npts)

# perform the smoothing
d = 4
lmbd = 1e-2

# constraints:
# set point 1 at 0
Aeq = np.zeros((1,npts))
Aeq[(0,0)] = 1.0
beq = np.array([0.0])
# make sure yhat is always increasing
D = ds.derivative_matrix(x,1)
bl = np.zeros((npts-1,1))
# make sure that yhat is concave
D2 = ds.derivative_matrix(x,2)
bu2 = np.zeros((npts-2,1))
Ain = np.vstack((-D,D2))
bin = np.vstack((bl,bu2))

yh = ds.smooth_data_constr(x,y,d,lmbd,(Ain,bin),(Aeq,beq))

print('scaled regularization parameter =', lmbd)
print('standard deviation =', np.std(y-yh,ddof=1))

yp = np.dot(ds.derivative_matrix(x,1),y)
y2p = np.dot(ds.derivative_matrix(x,2),y)
yhp = np.dot(ds.derivative_matrix(x,1),yh)
yh2p = np.dot(ds.derivative_matrix(x,2),yh)

figure(1)
cla()
plot(x,y,'ow',x,yh,'-b',x,yt,'-r')
legend(['scattered','smoothed','true'],loc='best',numpoints=1)
xlabel('x')
ylabel('y')
show()
draw()

figure(2)
cla()
plot(x[1:],yp,'ow',x[1:],yhp,'-b',x,ytp,'-r')
ylim( np.min(yhp)-np.abs(np.min(yhp)), np.max(yhp)*2 )
legend(['scattered','smoothed','true'],loc='best',numpoints=1)
xlabel('x')
ylabel("y'")
show()
draw()

figure(3)
cla()
plot(x[1:-1],y2p,'ow',x[1:-1],yh2p,'-b',x,yt2p,'-r')
ylim( np.min(yh2p)-np.abs(np.min(yh2p)), np.max(yh2p)+np.abs(np.min(yh2p)) )
legend(['scattered','smoothed','true'],loc='best',numpoints=1)
xlabel('x')
ylabel("y''")
show()
draw()


import numpy as np
from helpers import *
from costs import *
from gradients import *

def gradient_descent(y, tx, initial_w, max_iters, gamma, verbose = False):
    """
    Gradient descent algorithm.

    Parameters
    ----------
    y : Vector
        Output.
    tx : Matrix
        Input.
    initial_w : Vector
        Initial weights.
    max_iters : Scalar
        Maximum number of iterations.
    gamma : Scalar
        Learning rate.
    verbose : TYPE, optional
        Print GD steps. The default is False.

    Returns
    -------
    vector of loss and vector of weights for each iteration.

    """
    # Define parameters to store w and loss.
    ws = [initial_w]
    w = initial_w
    losses = []

    for n_iter in range(max_iters):
        # Compute gradient and loss
        g = mse_grad(y, tx, w)
        loss = mse(y, tx, w)
        # Update the weights
        w = w - gamma * g
        w = w.ravel()
        # Store the weights and loss
        ws.append(w)
        losses.append(loss)

        if verbose:
            print("Gradient descent({bi}/{ti}): loss={l}, w0={w0}, w1={w1}".format(
                bi=n_iter, ti=max_iters - 1, l=loss, w0=w[0], w1=w[1]))

    return losses, ws

def gradient_descent_logistic(y, tx, initial_w, max_iters, gamma, verbose = False):
    """
    Gradient descent algorithm for logistic regression.

    Parameters
    ----------
    y : Vector
        Output.
    tx : Matrix
        Input.
    initial_w : Vector
        Initial weights.
    max_iters : Scalar
        Maximum number of iterations.
    gamma : Scalar
        Learning rate.
    verbose : Boolean, optional
        Print GD steps. The default is False.

    Returns
    -------
    losses : Vector
        Loss for each iteration.
    ws : Vector
        Weight vector for each iteration.

    """
    # Define parameters to store w and loss.
    ws = [initial_w]
    w = initial_w
    losses = []

    for n_iter in range(max_iters):
        # Compute gradient and loss
        g = logistic_grad(y, tx, w)
        loss = logistic_error(y, tx, w)

        # Update the weights
        w = w - gamma * g
        w = w.ravel()
        # Store the weights and loss
        ws.append(w)
        losses.append(loss)

        if verbose:
            print("Gradient descent({bi}/{ti}): loss={l}, w0={w0}, w1={w1}".format(
                bi=n_iter, ti=max_iters - 1, l=loss, w0=w[0], w1=w[1]))

    return losses, ws

def reg_gradient_descent_logistic(y, tx, initial_w, max_iters, gamma, lambda_, reg, verbose=False,
                                  early_stopping=True, tol=0.0001, patience=5):
    """
    Regularized logistic regression with gradient descent.

    Parameters
    ----------
    y : Vector
        Output.
    tx : Matrix
        Input.
    initial_w : Vector
        Inital weights.
    max_iters : Scalar
        Maximum iterations.
    gamma : Scalar
        Learning rate.
    lambda_ : Scalar
        Regularization.
    reg : Integer
        l1 or l2 regularizarion.
    verbose : Boolean, optional
        Print steps. The default is False.
    early_stopping : Boolean, optional
        Stop early if no improvement in loss. The default is True.
    tol : Scalar, optional
        Minimum the loss needs to decrease by. The default is 0.0001.
    patience : Integer, optional
        Change needs to be seen in this number of iterations. The default is 10.

    Returns
    -------
    losses : Vector
        History of losses.
    ws : Vector
        History of weights.

    """

    # Define parameters to store w and loss.
    ws = [initial_w]
    w = initial_w
    losses = []

    for n_iter in range(max_iters):
        # Compute gradient and loss
        g = reg_logistic_grad(y, tx, w, lambda_, reg)
        loss = reg_logistic_error(y, tx, w, lambda_, reg)

        # Update the weights
        w = w - gamma * g
        w = w.ravel()
        # Store the weights and loss
        ws.append(w)
        losses.append(loss)

        if verbose:
            print("Gradient descent({bi}/{ti}): loss={l}, w0={w0}, w1={w1}".format(
                bi=n_iter, ti=max_iters - 1, l=loss, w0=w[0], w1=w[1]))

        if (early_stopping) and (n_iter > patience):
            # Check if loss has improved by tol in last patience iters
            l_pat = reg_logistic_error(y, tx, ws[-patience], lambda_, reg)
            l_1 = reg_logistic_error(y, tx, ws[-1], lambda_, reg)
            try: # If we get invaluid value in the log.
                if ((l_pat - l_1) < tol):
                    print(f"Stopped after {n_iter} it.")
                    break
            except exception as e:
                print(e)
                break

    return losses, ws


#Create a 3x3 identity matrix

import numpy as np

vector = np.zeros((8,8))
vector[1::2,::2]=1
vector[::2,1::2]=1
print(vector)

'''
Output

[[0. 1. 0. 1. 0. 1. 0. 1.]
 [1. 0. 1. 0. 1. 0. 1. 0.]
 [0. 1. 0. 1. 0. 1. 0. 1.]
 [1. 0. 1. 0. 1. 0. 1. 0.]
 [0. 1. 0. 1. 0. 1. 0. 1.]
 [1. 0. 1. 0. 1. 0. 1. 0.]
 [0. 1. 0. 1. 0. 1. 0. 1.]
 [1. 0. 1. 0. 1. 0. 1. 0.]]

'''


"""
Tests for Linear Regression
"""

import numpy as np
from pyai.ml.linear_regression import (
    linear_regression_normal_equation,
    linear_regression_sgd,
    plot_results,
    mean_squared_error,
)

def test_linear_regression():
    """Test Linear Regression"""

    # Toy dataset with only one feature
    samples = np.array(
        [
            [3.3],
            [4.4],
            [5.5],
            [6.71],
            [6.93],
            [4.168],
            [9.779],
            [6.182],
            [7.59],
            [2.167],
            [7.042],
            [10.791],
            [5.313],
            [7.997],
            [3.1],
        ]
    )

    # Expected results
    targets = np.array(
        [
            [1.7],
            [2.76],
            [2.09],
            [3.19],
            [1.694],
            [1.573],
            [3.366],
            [2.596],
            [2.53],
            [1.221],
            [2.827],
            [3.465],
            [1.65],
            [2.904],
            [1.3],
        ]
    )

    n_samples = samples.shape[0]

    # Add x0 = 1 to each sample for bias weight
    x_train = np.c_[np.ones((n_samples, 1)), samples]
    assert x_train.shape == (n_samples, samples.shape[1] + 1)

    y_pred_ne = linear_regression_normal_equation(x_train, targets)
    y_pred_sgd = linear_regression_sgd(x_train, targets)

    mse_ne = mean_squared_error(targets, y_pred_ne)
    mse_sgd = mean_squared_error(targets, y_pred_sgd)

    assert mse_ne <= 0.16892
    # Given its stochastic nature and the small number of samples,
    # results are sometimes poor with SGD
    assert mse_sgd < 2
    # When applicable, Normal Equation offers the best possible results
    assert mse_ne <= mse_sgd

    result_list = {
        "Normal Equation": y_pred_ne,
        "SGD": y_pred_sgd,
    }
    plot_results(samples, targets, result_list)


#!/usr/bin/env python
# coding: utf-8

# # Example of performing linear least squares fitting

# In[11]:

get_ipython().run_line_magic('matplotlib', 'inline')
import matplotlib.pyplot as plt
import numpy as np

# ### Now let's generate some random data about a trend line

# In[12]:

#set a random number seed
np.random.seed(119)

#set number of data points
npoints = 50

#set x
x = np.linspace(0,10.,npoints)

#set slope, intercept, and scatter rms
m = 2.0
b = 1.0
sigma = 2.0

#generate y points
y = m*x + b + np.random.normal(scale=sigma,size=npoints)
y_err = np.full(npoints,sigma)
#npfull makes an array that is full to npoints with sigma at every value

# ### Let's just plot the data first

# In[13]:

f = plt.figure(figsize=(7,7))
plt.errorbar(x,y,sigma,fmt='o')
plt.xlabel('x')
plt.ylabel('y')

# ### Method #1, polyfit()

# In[14]:

m_fit, b_fit = np.poly1d(np.polyfit(x,y,1,w=1./y_err)) #weight with uncertainties
#m_fit, b_fiot means that we are taking the first two values given by np.poly1 and assigning them to m and b fit
#polyfit provides polynomial fit to y as a function of x to the order of 1
#only provide one power for "inverse variance weighting"?
print(m_fit, b_fit)

y_fit = m_fit * x + b_fit

# In[15]:

f = plt.figure(figsize=(7,7))
plt.errorbar(x,y,yerr=y_err, fmt='o',label='data')
plt.plot(x,y_fit,label='fit')
plt.xlabel('x')
plt.ylabel('y')
plt.legend(loc=2,frameon=False)

# ### Method #2, scipy + optimize

# In[17]:

#scipy method is more flexible,
#but the polyfit gives an analytical (formal) answer

# In[18]:

#import optimize from scipy
from scipy import optimize

#define the function to fit
def f_line(x,m,b):
    return m*x + b

#perform the fit
params, params_cov = optimize.curve_fit(f_line,x,y,sigma=y_err)

m_fit = params[0]
b_fit = params[1]
print(m_fit,b_fit)

# ### We can perform much more complicated fits

# In[28]:

#redefine x and y
npoints = 50
x = np.linspace(0,2*np.pi,npoints)

#make y a complicated function
a = 3.4
b = 2.1
c = 0.27
d = -1.3
sig = 0.6

y = a * np.sin(b*x + c) + d + np.random.normal(scale=sig, size=npoints)
y_err = np.full(npoints,sig)

f = plt.figure(figsize=(7,7))
plt.errorbar(x,y,yerr=y_err,fmt='o')
plt.xlabel('x')
plt.ylabel('y')

#He redefined y_err I think, but not sure

# In[29]:

#import optimize from scipy
from scipy import optimize

#define the function to fit
def f_line(x, a, b, c, d):
    return a * np.sin( b*x + c) + d

#perform the fit
params, params_cov = optimize.curve_fit(f_line,x,y,sigma=y_err,p0=[1,2.,0.1,-0.1])

a_fit = params[0]
b_fit = params[1]
c_fit = params[2]
d_fit = params[3]

print(a_fit,b_fit,c_fit,d_fit)

y_fit = a_fit * np.sin(b_fit * x + c_fit) + d_fit

# In[30]:

f = plt.figure(figsize=(7,7))
plt.errorbar(x,y,yerr=y_err,fmt='o',label='data')
plt.plot(x,y_fit,label='fit')
plt.xlabel('x')
plt.ylabel('y')
plt.legend(loc=0,frameon=False)

# In[ ]:


# Mathematics > Linear Algebra Foundations > Eigenvalue of matrix #4
# Basic problems related to eigenvalues.
#
# https://www.hackerrank.com/challenges/eigenvalues-of-matrix-4/problem
#

import numpy as np

A = np.matrix([[ 2, -1],
               [-1,  2]])

I = np.identity(np.linalg.matrix_rank(A))

print(np.linalg.eigvals(np.linalg.inv(A)))

print(np.linalg.eigvals(A - 4 * I))


import math as math
import numpy as np
# Main Metrics

#   Mean absolute error (MAE)
def mean_absolute_error(y, yi):
    sum = 0
    for v,vi in y,yi:
        sum += math.fabs(v - vi)
    result = sum / len(y)
    return result

#   Median absolute error
def median_absolute_error(y, yi):
    return np.median(math.fabs(y-yi))

#   Mean squared error (MSE)
def mean_squred_error(y, yi):
    sum = 0
    for v, vi in y, yi:
        sum += math.sqrt(v - vi)
    result = sum / len(y)
    return result

# Classification Metrics
#   Accuracy
def accuracy(y, yi):
    sum = 0
    for v, vi in y, yi:
        sum += 1 if v == vi else 0
    result = sum / len(y)
    return result

#   Precision score, recall and F-measure
def precision(true_positives, false_positives):
    return true_positives / (true_positives + false_positives)

def recall(true_positives, false_negatives):
    return true_positives / (true_positives + false_negatives)

# prec =  precision, rec = recall
def f_measure(b2, prec, rec):
    return (1 + math.sqrt(b2))*(prec * rec) / (math.sqrt(b2) * prec + rec)


from scipy import stats
import matplotlib.pyplot as ___
import ___ as pd
import numpy as ___

# read in the data
dementia_df = ___.___(___)

# cross table with count of observation for each CDR&Educ combination
ct = pd.___(
    index=dementia_df["Educ"],
    columns=dementia_df["CDR"])

print("Cross table:")
display(ct)

# extract the test results
chisq_stat, p_val, dof, expctd = stats.chi2_contingency(
    observed=___)

alpha = ___ # significance level
# percent point function (P[Chi-squared]<q)
threshold = stats.chi2.ppf(q=___, df=___) # chi-squared critical

# values for the PDF plot
x = np.linspace(0, 30, 1000)
y = stats.chi2.___(x=x, df=dof) # probability density function

plt.figure(figsize=(9,5), facecolor="white")
plt.plot(x, y, color="black", linewidth=3, label="Null distribution")
plt.axvline(x=chisq_stat, color="blue", label="Observed value")
plt.axvline(x=threshold, color="red", label="Threshold value")
plt.fill_between(
    x[x>=threshold], y[x>=threshold],
    color='red', alpha=0.5,
    label="Rejection area (alpha)")
plt.fill_between(
    x[x>=chisq_stat], y[x>=chisq_stat],
    color='blue', alpha=0.5, label="p-value")
plt.legend()
plt.xlabel("Chi squared")
plt.ylabel("Density")
plt.title("Null Distribution")
plt.show()

print(f"Chi-squared={chisq_stat:.2f}, p-val={p_val:.2f}")
if p_val ___ alpha:
    print("Reject the H_0 in favor of H_A")
else:
    print("Fail to reject the H0")


import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

figure = plt.figure()
# axes = Axes3D(figure)

R = 10
t = np.linspace(-np.pi, np.pi, 100)
X = R * np.cos(t)
Y = R * np.sin(t)
# X, Y = np.meshgrid(X, Y)
# Z = R**2 - X**2 - Y**2
X = R * np.outer(np.cos(t), np.sin(t))
Y = R * np.outer(np.sin(t), np.sin(t))
Z = R * np.outer(np.ones(np.size(t)), np.cos(t))
"""
    不明白为什么这样绘制出来的是球形
"""
axes = figure.add_subplot(121, projection='3d')
axes.plot_surface(X, Y, Z, rstride=4, cstride=4, color='b')
axes = figure.add_subplot(122, projection='3d')
axes.plot_wireframe(X, Y, Z)
plt.show()


import numpy as np
import random

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

def dsigmoid(y): # definition of derivative of sigmoid, in a format to solve incompatability issues with numpy matricies
    return np.multiply(y, 1.0 - y)

def train_autoencoder(input_data, desired_output, input_layer_size, hidden_layer_size, output_layer_size, L, alpha, iterations):
    '''
    Initial method to train and validate the neural network as an auto encoder by updating weight and bias matrices between layers

    Inputs: training / input data (as matrix); desired output (as matrix); size of input, hidden, output layers
            weight decay parameter lambda (L); learning rate alpha (a); total iterations
    Outputs: final output layer, average sum of errors
    '''
    np.random.seed(4) # Keep random seed constant for testing purposes
    m = input_data.shape[0] # number of training examples

    # Initialize random weights with mean 0
    input_weights = 2 * np.random.random((input_layer_size, hidden_layer_size)) - 1
    hidden_weights = 2 * np.random.random((hidden_layer_size, output_layer_size)) - 1

    # Initialize bias 'matrices' of zeros
    input_bias = np.zeros((input_layer_size,hidden_layer_size))
    hidden_bias = np.zeros((input_layer_size,output_layer_size))

    # Iterate to train weight matrices
    for i in range(0, iterations):

        # input data = input layer; contains all training data, simultaneously
        input_layer = input_data

        # Given the training data and weight matrix, tranform via the dot product, add bias matrix
        input_transform = np.dot(input_layer, input_weights) + input_bias
        # Pass through the activation function
        hidden_layer = sigmoid(input_transform)

        # Again, add bias and propagate predicted values through the weights between hidden + output layers
        hidden_transform = np.dot(hidden_layer, hidden_weights) + hidden_bias
        output_layer = sigmoid(hidden_transform)

        # Get the difference between the desired output and the result from the autoencoder
        output_layer_error = desired_output - output_layer

        # Print the error every so often
        if (i == 0 or (i % (iterations / 10)) == 0):
            print('Iteration: ' + str(i) + '\n\tAverage Error: ' + str(np.mean(np.abs(output_layer_error))) + '\n')

        # Take the error weighted derivative to avoid changing weights that lead to post-sigmoid values close to either 0 or 1
        # Because these are probably starting to converge -> focus effort on changing ambiguous (~ 0.5) values
        output_layer_delta = output_layer_error * dsigmoid(output_layer)

        # Backpropagate to determine the error in the hidden layer
        hidden_layer_error = output_layer_delta.dot(hidden_weights.T)

        # Another error-weighted derivative
        hidden_layer_delta = hidden_layer_error * dsigmoid(hidden_layer)

        # Update the weights for each layer in batch
        hidden_weights += alpha * (((1 / m ) * (hidden_layer.T.dot(output_layer_delta))) + L * hidden_weights)
        hidden_bias += (alpha / m) * output_layer_delta

        input_weights += alpha * (((1 / m ) * (input_layer.T.dot(hidden_layer_delta))) + L * input_weights)
        input_bias += (alpha / m) * hidden_layer_delta

    print('After training the encoder for ' + str(iterations) + ' iterations, \n\tAverage Error: ' + str(np.mean(np.abs(output_layer_error))))
    print('input data: ')
    print(input_data)
    print('output data: ')
    print(output_layer)
    return output_layer, np.mean(np.abs(output_layer_error))

def train_neuralnet(pos_data, neg_data, input_layer_size, hidden_layer_size, L, alpha, iterations, convergence_criteria, print_output = False):
    '''
    Method to train the neural network using both positive and negative training data.
    For a given number of iterations, update weight and bias matrices between layers.
    Serves as a wrapper function for the actual unput of data into the network as defined by its weight and bias matrices.

    Inputs: positive / negative training data; size of input, hidden, output layers
            weight decay parameter lambda (L); learning rate alpha (a); total iterations; convergence criteria delta (improvement in average error)
    Outputs: avg positive error, avg negative error, input & hidden weight matricies, input and hidden bias matrices
    '''
    np.random.seed(4) # Keep random seed constant for testing purposes
    m = pos_data.shape[0] # number of training examples (assume for this case, equal amount of positive and negative data)

    # Initialize random weights with mean 0
    input_weights = 2 * np.random.random((input_layer_size, hidden_layer_size)) - 1
    hidden_weights = 2 * np.random.random((hidden_layer_size, 1)) - 1 # Output layer size is always 1

    # Initialize bias nodes
    input_bias = np.zeros((1,hidden_layer_size)) # One input at a time
    hidden_bias = np.zeros((1, 1)) # Output layer size 1

    # Keep track of running error
    pos_error = 1.0
    neg_error = 1.0

    # Train network one data point at a time, and alternate between positive and negative examples
    for i in range(0, iterations):
        training_order = random.sample(range(0,m), m)  # pick random order to train each iteration

        # Keep track of error accrued every iteration
        pos_errors = []
        neg_errors = []

        for j in training_order: # Train with both positive and negative data iteratively for entirety of both datasets
            sample_input = np.asmatrix(pos_data[j]) # Test one positive input
            last_pos_error, input_weights, hidden_weights, input_bias, hidden_bias = NN_input(sample_input, input_weights, hidden_weights, input_bias, hidden_bias, m, L, alpha, True, True)

            sample_input = np.asmatrix(neg_data[j]) # Test one negative input
            last_neg_error, input_weights, hidden_weights, input_bias, hidden_bias = NN_input(sample_input, input_weights, hidden_weights, input_bias, hidden_bias, m, L, alpha, False, True)

            pos_errors.append(last_pos_error)
            neg_errors.append(last_neg_error)

        # Calculate average errors, delta from training cycle
        avg_pos_error = sum(pos_errors) / float(len(pos_errors))
        avg_neg_error = sum(neg_errors) / float(len(neg_errors))
        pos_delta = np.abs(avg_pos_error - pos_error) / pos_error
        neg_delta = np.abs(avg_neg_error - neg_error) / neg_error

        if print_output and (i == 0 or (i % (iterations / 100)) == 0):  # Print progress every so often
            print('Iteration: ' + str(i) + '\n\tAvg Pos Error: ' + str(avg_pos_error) + '\n\tAvg Neg Error: ' + str(avg_neg_error))
            print('\tPos Err Delta: ' + str(pos_delta))
            print('\tNeg Err Delta: ' + str(neg_delta))

        if print_output and pos_delta <= convergence_criteria and neg_delta <= convergence_criteria: # Successive iterations have marginal gains; stop training
            print('Convergence criteria of ' + str(convergence_criteria) + ' met at iteration ' + str(i))
            print('Average Positive Error: ' + str (avg_pos_error))
            print('Average Negative Error: ' + str (avg_neg_error))
            return avg_pos_error, avg_neg_error, input_weights, hidden_weights, input_bias, hidden_bias
        else: # Keep going until convergence or iterations are complete
            pos_error = avg_pos_error
            neg_error = avg_neg_error

    return pos_error, neg_error, input_weights, hidden_weights, input_bias, hidden_bias

def test_neuralnet(pos_data, neg_data, input_weights, hidden_weights, input_bias, hidden_bias, m , L, alpha):
    '''
    Basic funciton to test the average errors in the ANN with both positive and negative test data

    Inputs: positive & negative test data (as 34-bit vectors in rows; sample count must be the same), input & hidden weight matrices, input and bias weight matrices, m, L, alpha
    Outputs: total average positive and negative errors resulting from input data being run through network
    '''
    pos_errors = [] # Store all accrued errors
    neg_errors = []

    # Iterate through all positive and negative test data
    for index in range(0,pos_data.shape[0]):
        pos_input = np.asmatrix(pos_data[index])
        neg_input = np.asmatrix(neg_data[index])

        # Run through neural network, get resulting error
        last_pos_error = NN_input(pos_input, input_weights, hidden_weights, input_bias, hidden_bias, m, L, alpha, True, False)
        last_neg_error = NN_input(neg_input, input_weights, hidden_weights, input_bias, hidden_bias, m, L, alpha, False, False)

        # Add errors to list
        pos_errors.append(last_pos_error)
        neg_errors.append(last_neg_error)

    # After all iterations are complete, calculate average errors
    avg_pos_error = sum(pos_errors) / float(len(pos_errors))
    avg_neg_error = sum(neg_errors) / float(len(neg_errors))

    return avg_pos_error, avg_neg_error

def NN_input(sample_input, input_weights, hidden_weights, input_bias, hidden_bias, m, L, alpha, pos_sample = True, update = False):
    '''
    Funtion to take sample input (one by one) and run them through the neural network as defined by the weight and bias matrices.
    If the update flag is True, then it will update these matrices through back propagation and return them; otherwise it simply returns the error.

    Input:  sample input (vector of 34 bits of 0,1), input & hidden matrices, weight & bias matricies,
            total sample size, lambda, alpha for training purposes, a flag to define if desired output is 0 or 1, update flag
    Output: If updating – output error, updated input and hidden weight matrices, updated input and hidden bias matrices.
            If not updating – output error
    '''
    if pos_sample: # Desired output of positive value = 1, negative value = 0
        desired_output = np.ones((1,1))
    else:
        desired_output = np.zeros((1,1))

    # Given the sample input and weight matrix, tranform via the dot product, add bias matrix
    input_transform = np.dot(sample_input, input_weights) + input_bias

    # Pass through the activation function
    hidden_layer = sigmoid(input_transform)

    # Again, add bias and propagate predicted values through the weights between hidden + output layers
    hidden_transform = np.dot(hidden_layer, hidden_weights) + hidden_bias
    output = sigmoid(hidden_transform)

    # Get the difference between the desired output and the result from the autoencoder
    output_error = desired_output - output

    if update: # Update all weight and bias matrices and return these updates along with the error
        # Take the error weighted derivative to avoid changing weights that lead to post-sigmoid values close to either 0 or 1
        # Because these are probably starting to converge -> focus effort on changing ambiguous (~ 0.5) values
        output_delta = output_error * dsigmoid(output)

        # Backpropagate to determine the error in the hidden layer
        hidden_layer_error = output_delta.dot(hidden_weights.T)

        # Another error-weighted derivative
        hidden_layer_delta = np.multiply(hidden_layer_error, dsigmoid(hidden_layer))

        # Update the weights  and bias matrices for each layer
        hidden_weights += alpha * (((1 / m ) * (hidden_layer.T.dot(output_delta))) + L * hidden_weights)
        hidden_bias += (alpha / m) * output_delta

        input_weights += alpha * (((1 / m ) * (sample_input.T.dot(hidden_layer_delta))) + L * input_weights)
        input_bias += (alpha / m) * hidden_layer_delta

        return np.mean(np.abs(output_error)), input_weights, hidden_weights, input_bias, hidden_bias

    return np.mean(np.abs(output_error))    # Not updating, just return the error

def get_neuralnet_predictions(samples, input_weights, hidden_weights, input_bias, hidden_bias):
    '''
    Given a pre-trained neural network, take a series of samples and provide predictions as to their Rap1 binding likelihood

    Inputs: sample data (as 34-bit vectors in rows), input & hidden weight matrices, input and bias weight matrices
    Outputs: total average positive and negative errors resulting from input data being run through network
    '''
    output_list = [] # Store all predicted output values

    # Iterate through all test data
    for index in range(0,samples.shape[0]):
        sample_input = np.asmatrix(samples[index]) # Pick one row at a time

        # Given the sample input and weight matrix, tranform via the dot product, add bias matrix
        input_transform = np.dot(sample_input, input_weights) + input_bias

        # Pass through the activation function
        hidden_layer = sigmoid(input_transform)

        # Again, add bias and propagate predicted values through the weights between hidden + output layers
        hidden_transform = np.dot(hidden_layer, hidden_weights) + hidden_bias
        output = float(sigmoid(hidden_transform))

        # Get the difference between the desired output and the result from the autoencoder
        output_list.append(output)

    return output_list


#!/usr/bin/env python

import math
import numpy as np

import matplotlib as mpl
mpl.use('Agg')
import matplotlib.pyplot as plt

def norm_pdf(x, mu, sigma):
    """Return the normal probability distribution function."""

    prefac = 1 / (sigma * math.sqrt(2 * math.pi))
    expt = np.exp(-(x - mu)**2 / (2 * sigma**2))

    return prefac * expt

### example normal distributions
### https://en.wikipedia.org/wiki/Normal_distribution

fig, ax = plt.subplots()

x = np.linspace(-5, 5, 300)

ax.plot(x, norm_pdf(x, mu=0, sigma=math.sqrt(0.2)), label=r'$\mu=0,\,\sigma^2=0.2$', color='blue')
ax.plot(x, norm_pdf(x, mu=0, sigma=math.sqrt(1.0)), label=r'$\mu=0,\,\sigma^2=1.0$', color='red')
ax.plot(x, norm_pdf(x, mu=0, sigma=math.sqrt(5.0)), label=r'$\mu=0,\,\sigma^2=5.0$', color='yellow')
ax.plot(x, norm_pdf(x, mu=-2, sigma=math.sqrt(0.5)), label=r'$\mu=-2,\,\sigma^2=0.5$', color='green')

ax.set_xlabel(r'$x$')
ax.set_ylabel(r'$\varphi_{\mu,\sigma^2}(x)$')

ax.legend(fancybox=True, loc='best', framealpha=0.50)
fig.savefig('norm_pdf.pdf', bbox_inches='tight')

plt.close(fig)


import matplotlib.pyplot as plt
import numpy as np

N = 500
k = 3
plt.figure(1)

s = np.exp(1j*2*np.pi*k/N*np.arange(-N/2, N/2))
plt.subplot(1, 2, 1)
plt.plot(np.arange(-N/2, N/2), np.real(s), lw=2)
plt.axvline(0, color='g', lw=2)
plt.axis([-N/2,N/2,-1,1])
plt.title ('cosine (even)')
plt.subplot(1, 2, 2)
plt.plot(np.arange(-N/2, N/2), np.imag(s), lw=2)
plt.axvline(0, color='g', lw=2)
plt.axis([-N/2,N/2,-1,1])
plt.title ('sine (odd)')

plt.tight_layout()
plt.savefig('even-odd.png')
plt.show()


import numpy as np

def gauss_kernel(sigma, sample_rate, duration):
    """
    Creates a Gaussian kernel centered in a vector of given duration.

    :param sigma: the standard deviation of the kernel in seconds
    :param sample_rate: the temporal resolution of the kernel in Hz
    :param duration: the desired duration of the kernel in seconds, (in general at least 4 * sigma)

    :return: the kernel as numpy array
    """
    l = duration * sample_rate
    x = np.arange(-np.floor(l / 2), np.floor(l / 2)) / sample_rate
    y = (1 / (np.sqrt(2 * np.pi) * sigma)) * np.exp(-(x ** 2 / (2 * sigma ** 2)))
    y /= np.sum(y)
    return y


#!/usr/bin/env python3
# -*- coding: utf-8 -*-
#
# Date        : Jun 13, 2017 Tue
# Last update :
#
# Required answer: 292,201,338
# Imports
from scipy.misc import comb
import sympy
import math

# How to calculate combinations in python?
def calculate_probability():
    prob1 = comb(69,5)
    prob2 = sympy.binomial(26,1)
    prob = prob1 * prob2

    print('prob =  {0:,}'.format(int(prob)))
    return int(prob)

def years_to_win_lottery():
    prob = calculate_probability()
    century, day = divmod(prob,36500)
    year,days = divmod(prob,365)

    print('If you buy a ticket everyday and you are immortal, you may win\
    \b\b\b\b the jackpot after %d centuries and %d days!'%(century,day))

    print('\nIf you want to count in years, you will win lottery \
    in %d years and %d days.'%(year,days))

if __name__ == '__main__':
    prob = calculate_probability()
    years_to_win_lottery()

# From wikipedia:
# Drawings are held Wednesday and Saturday evenings at 10:59 p.m. Eastern Time.
# Since October 7, 2015, the game has used a 5/69 (white balls) + 1/26
# (Powerballs) matrix from which winning numbers are chosen, resulting
# in odds of 1 in 292,201,338 of winning a jackpot per play.

# 1 yr = 365 days
# 1 century = 100 * 365 = 36500 days
# 1 millinium = 1000 * 365 = 365000


import numpy as np
import time

def hms2dec(h,m,s):
  return 15*(h + (m/60) + (s/3600))

def dms2dec(d,m,s):
  if d>=0:
    return (d + (m/60) + (s/3600))
  return (d - (m/60) - (s/3600))

def angular_dist(r1, d1, r2, d2):
  a = (np.sin(np.abs(d1 - d2)/2))**2
  b = np.cos(d1)*np.cos(d2)*np.sin(np.abs(r1 - r2)/2)**2
  d = 2*np.arcsin(np.sqrt(a + b))
  return d

def crossmatch(cat1, cat2, max_dist):

  matches = []
  no_matches = []

  start = time.perf_counter()

  cat1 = np.radians(cat1)
  cat2 = np.radians(cat2)
  max_dist = np.radians(max_dist)

  cat2_dec = cat2[:, 1]
  sorted_indexes = np.argsort(cat2_dec)
  sorted_cat2_dec = np.sort(cat2_dec)

  id_1 = 0
  for i in cat1:
      best = (0,0,max_dist+1)

      initial = np.searchsorted(sorted_cat2_dec, i[1] - max_dist, side='left')
      last = np.searchsorted(sorted_cat2_dec, i[1] + max_dist, side='left')

      for j in range(initial,last):

        dist = angular_dist(i[0], i[1], cat2[sorted_indexes[j]][0], cat2[sorted_indexes[j]][1])
        if dist<=best[2]:
          best = (id_1,sorted_indexes[j],dist)

      if best[2]<=max_dist:
         matches.append(best)
      else:
         no_matches.append(id_1)
      id_1+=1

  time_taken = time.perf_counter() - start
  return (matches, no_matches, time_taken)

if __name__ == '__main__':

  cat1 = np.array([[180, 30], [45, 10], [300, -45]])
  cat2 = np.array([[180, 32], [55, 10], [302, -44]])
  matches, no_matches, time_taken = crossmatch(cat1, cat2, 5)
  print('matches:', matches)
  print('unmatched:', no_matches)
  print('time taken:', time_taken)

  # A function to create a random catalogue of size n
  def create_cat(n):
    ras = np.random.uniform(0, 360, size=(n, 1))
    decs = np.random.uniform(-90, 90, size=(n, 1))
    return np.hstack((ras, decs))

  # Test function on random inputs
  np.random.seed(0)
  cat1 = create_cat(10)
  cat2 = create_cat(20)
  matches, no_matches, time_taken = crossmatch(cat1, cat2, 5)
  print('matches:', matches)
  print('unmatched:', no_matches)
  print('time taken:', time_taken)


"""
Objetivo: Resolver questão 1a do terceiro laboratorio.
"""
import matplotlib.pyplot as plt
import numpy as np
from Newton_Raphson import newton_raphson

y = lambda x: x ** 3 - 4 * x + 1  # f(x) = x^3 -4x + 1
y_linha = lambda x: 3 * x ** 2 - 4  # f'(x) = 3x^2 -4
ini = -1  # ponto inicial
#Verificar o arquivo Nexton-Raphson.py para entender o funcionamento dessa função
resp = newton_raphson(ini, y, y_linha, 'q1')
print(f'Para essa situação levamos {resp["iterações"] - 1} iterações e temos como valor de f(x) = {resp["modulo"]}')
print('Abaixo seguem os graficos dos valores de f(x) e dos erros, respectivamente:')

# Grafico com os valores de f(x)
x_a = np.linspace(-1.5, 3.5, 30)    #valores escolhidos para melhor visualização do grafico
y_a = [y(i) for i in x_a]
plt.style.use('ggplot')
plt.figure(figsize=(7, 5))
plt.title('F(x) por X')
plt.xlabel('Valores de x')
plt.ylabel('Valores de f(x)')
#função com valores entre -1.5 e 3.5
plt.plot(x_a, y_a, label='Função exata')
#Nossa aproximação Newton-Raphson por iteração
plt.plot(resp['valores'], resp['valores_função'], label='Valores de Newton-Raphson por iteração')
plt.tight_layout()
plt.legend(loc='best')
plt.show()

# Grafico com os valores dos erros
plt.style.use('ggplot')
plt.figure(figsize=(7, 5))
plt.title('Erro por Iterações')
plt.xlabel('Numero de iterações')
plt.ylabel('Valores dos erros')
plt.plot([i for i in range(1, resp['iterações'])], resp['erros'])
plt.tight_layout()
plt.show()


# from
# http://stackoverflow.com/questions/20924085/python-conversion-between-coordinates

import numpy as np

def rect(r, theta):
    """theta in degrees

    returns tuple; (float, float); (x,y)
    """
    x = r * np.cos(np.radians(theta))
    y = r * np.sin(np.radians(theta))
    return x,y

def polar(x, y):
    """returns r, theta(degrees)
    """
    lx = np.array(x)
    ly = np.array(y)
    r = np.sqrt(lx**2 + ly**2)
    theta = np.degrees(np.arctan(ly / lx))
    return r, theta

class Point(object):
    def __init__(self, x=None, y=None, r=None, theta=None):
        """x and y or r and theta(degrees)
        """
        if x and y:
            self.c_polar(x, y)
        elif r and theta:
            self.c_rect(r, theta)
        else:
            raise ValueError('Must specify x and y or r and theta')
    def c_polar(self, x, y, f = polar):
        self._x = x
        self._y = y
        self._r, self._theta = f(self._x, self._y)
        self._theta_radians = np.radians(self._theta)
    def c_rect(self, r, theta, f = rect):
        """theta in degrees
        """
        self._r = r
        self._theta = theta
        self._theta_radians = np.radians(theta)
        self._x, self._y = f(self._r, self._theta)
    def setx(self, x):
        self.c_polar(x, self._y)
    def getx(self):
        return self._x
    x = property(fget = getx, fset = setx)
    def sety(self, y):
        self.c_polar(self._x, y)
    def gety(self):
        return self._y
    y = property(fget = gety, fset = sety)
    def setxy(self, x, y):
        self.c_polar(x, y)
    def getxy(self):
        return self._x, self._y
    xy = property(fget = getxy, fset = setxy)
    def setr(self, r):
        self.c_rect(r, self._theta)
    def getr(self):
        return self._r
    r = property(fget = getr, fset = setr)
    def settheta(self, theta):
        """theta in degrees
        """
        self.c_rect(self._r, theta)
    def gettheta(self):
        return self._theta
    theta = property(fget = gettheta, fset = settheta)
    def set_r_theta(self, r, theta):
        """theta in degrees
        """
        self.c_rect(r, theta)
    def get_r_theta(self):
        return self._r, self._theta
    r_theta = property(fget = get_r_theta, fset = set_r_theta)
    def __str__(self):
        return '({},{})'.format(self._x, self._y)


import numpy as np

import openmdao.api as om

class LiftEquilibriumComp(om.ExplicitComponent):
    """
    Compute the rates of TAS and flight path angle required to match a given
    flight condition.
    """

    def initialize(self):
        self.options.declare('num_nodes', types=int)

        self._g = 9.80665

    def setup(self):
        n = self.options['num_nodes']

        # Parameter inputs
        self.add_input(name='CT', shape=(n,), desc='thrust coefficient', units=None)
        self.add_input(name='W_total', shape=(n,), desc='total aircraft weight', units='N')
        self.add_input(name='alpha', shape=(n,), desc='angle of attack', units='rad')
        self.add_input(name='gam', shape=(n,), desc='flight path angle', units='rad')
        self.add_input(name='q', shape=(n,), desc='dynamic pressure', units='Pa')
        self.add_input(name='S', shape=(n,), desc='reference area', units='m**2')

        self.add_output(name='CL_eq', shape=(n,),
                        desc='lift coefficient required for steady, level flight',
                        units=None)

        ar = np.arange(n)

        self.declare_partials(of='CL_eq', wrt='CT', rows=ar, cols=ar)
        self.declare_partials(of='CL_eq', wrt='W_total', rows=ar, cols=ar)
        self.declare_partials(of='CL_eq', wrt='alpha', rows=ar, cols=ar)
        self.declare_partials(of='CL_eq', wrt='gam', rows=ar, cols=ar)
        self.declare_partials(of='CL_eq', wrt='q', rows=ar, cols=ar)
        self.declare_partials(of='CL_eq', wrt='S', rows=ar, cols=ar)

    def compute(self, inputs, outputs):
        CT = inputs['CT']
        W_total = inputs['W_total']
        alpha = inputs['alpha']
        gam = inputs['gam']
        q = inputs['q']
        S = inputs['S']

        sa = np.sin(alpha)
        cgam = np.cos(gam)

        qS = q * S

        outputs['CL_eq'] = W_total * cgam / qS - CT * sa

    def compute_partials(self, inputs, partials):
        CT = inputs['CT']
        W_total = inputs['W_total']
        alpha = inputs['alpha']
        gam = inputs['gam']
        q = inputs['q']
        S = inputs['S']

        ca = np.cos(alpha)
        sa = np.sin(alpha)
        cgam = np.cos(gam)
        sgam = np.sin(gam)

        qS = q * S

        partials['CL_eq', 'CT'] = -sa
        partials['CL_eq', 'W_total'] = cgam / qS
        partials['CL_eq', 'alpha'] = -CT * ca
        partials['CL_eq', 'gam'] = -W_total * sgam / qS
        partials['CL_eq', 'q'] = -W_total * cgam / (q**2 * S)
        partials['CL_eq', 'S'] = -W_total * cgam / (q * S**2)


import numpy as np

DTYPE = np.double

class SoftmaxUnit:
    """This class stores the activations of the RNN Unit"""

    def __init__(self, output_size, batch_size, dtype):
        self.p = np.zeros([output_size, batch_size], dtype=dtype)

    def forward_function(self, h, Woh, bo):
        """Returns p = softmax(Woh*h)"""
        self.p = np.matmul(Woh, h) + bo
        self.p -= np.max(self.p, 0)
        self.p[:] = np.exp(self.p)
        self.p /= np.sum(self.p, 0)
        return self.p

    def compute_loss(self, target):
        """Returns the cross entropy with respect to target"""
        return np.sum(np.log(self.p[target[0], list(range(self.p.shape[1]))]) * target[1])

    def backward_function(self, target, h, Woh, bo):
        """Returns dEdh, dEdWoh, and dEdbo where E = cross entropy"""
        dEdp = -self.p
        dEdp[target[0], list(range(dEdp.shape[1]))] += 1.0
        dEdp *= target[1]
        dWoh = np.matmul(dEdp, h.T)
        dbo = np.sum(dEdp, 1, keepdims=True)
        dEdh = np.matmul(dEdp.T, Woh)
        return dEdh.T, dWoh, dbo

def test_softmax_unit():
    """Tests for the gradient computation of the single SoftmaxUnit"""
    output_size = 10
    input_size = 5
    batch_size = 4
    Woh = np.zeros([output_size, input_size], dtype=DTYPE)
    Woh += np.random.uniform(-0.1, 0.1, Woh.shape)
    bo = np.zeros([output_size,1], dtype=DTYPE)
    bo += np.random.uniform(-0.1, 0.1, bo.shape)
    h = np.zeros([input_size, batch_size], dtype=DTYPE)
    h += np.random.uniform(-0.1, 0.1, [input_size, batch_size])
    target = [None] * 2
    target[0] = [2] * batch_size
    target[1] = [1.0] * batch_size
    target[1][0] = 0.0
    target[1][2] = 0.0
    print(target)

    softmax_unit = SoftmaxUnit(output_size, batch_size, DTYPE)

    # Exact gradient computation
    # p = softmax_unit.forward_function(h, Woh, bo)
    E = softmax_unit.compute_loss(target)
    _, dWoh, dbo = softmax_unit.backward_function(target, h, Woh, bo)

    # Numerical gradient computation
    epsilon = 1e-7
    numdWoh = np.zeros([output_size, input_size], dtype=DTYPE)
    for i in range(output_size):
        for j in range(input_size):
            newWoh = np.copy(Woh)
            newWoh[i, j] += epsilon

            # p = softmax_unit.forward_function(h, newWoh, bo)
            newE = softmax_unit.compute_loss(target)
            numdWoh[i, j] = (newE - E) / epsilon

    diff = abs(np.sum(numdWoh - dWoh))
    assert diff < 1e-4
    print('dWoh test passed! abs(expected - actual) =', diff)

    # Numerical gradient computation
    epsilon = 1e-7
    numdbo = np.zeros([output_size, 1],dtype=DTYPE)
    for i in range(output_size):
        newbo = np.copy(bo)
        newbo[i] += epsilon

        # p = softmax_unit.forward_function(h, Woh, newbo)
        newE = softmax_unit.compute_loss(target)
        numdbo[i] = (newE - E) / epsilon

    diff = abs(np.sum(numdbo - dbo))
    assert diff < 1e-4
    print('dbo test passed! abs(expected - actual) =', diff)

if __name__ == '__main__':
    test_softmax_unit()


# -*- coding: utf-8 -*-
"""
Created on Wed Feb 28 08:13:49 2018

@author: adrian
"""

import math
import sympy as sy

# Define the variable and the function to approximate
x = sy.Symbol('x')
f = sy.exp(x)

# Taylor approximation at x0 of the function 'function'
def taylor(function,x0,n):
    i = 0
    p = 0
    while i <= n:
        p = p + (function.diff(x,i).subs(x,x0))/(math.factorial(i))*(x-x0)**i
        i += 1
    return p

#n es número de términos y x es valor a evaluar
def evaluarTaylor(n,valorEvaluacion):
    func = str(taylor(f,0,n))
    x = valorEvaluacion
    return eval(func)

def aitken(x0,x1,x2,valorEvaluacion,E):
    err = 1
    j = 3
    i = 1
    print("\t      Término \t     Iteración \t Error relativo")
    a0 = x2-((x2-x1)**2)/(x2-2*x1+x0) #fórmula
    print("\t",a0,"\t                ",i)
    while(err >= E):
        a1 = a0
        x0 = evaluarTaylor(j-2,valorEvaluacion)
        x1 = evaluarTaylor(j-1,valorEvaluacion)
        x2 = evaluarTaylor(j,valorEvaluacion)
        a0 = x2-((x2-x1)**2)/(x2-2*x1+x0) #fórmula
        err = (abs(a0-a1)/abs(a0))
        j += 1
        i += 1
        print("\t",a0,"\t",i,"\t",err)

#func = str(taylor(f,0,5))
#print("Términos de la serie tomados: ",func)

valorEvaluacion = 1

x0 = evaluarTaylor(0,valorEvaluacion)
x1 = evaluarTaylor(1,valorEvaluacion)
x2 = evaluarTaylor(2,valorEvaluacion)

E = 1.e-15

aitken(x0, x1, x2, valorEvaluacion,E)


#!/usr/bin/env python
# -*- coding: utf-8 -*-

import numpy as np
import matplotlib.pylab as plt
import matplotlib.image as mpimg
plt.rcParams['figure.figsize'] = 15, 6
#plt.rcParams['figure.dpi'] = 300

NUMBER = 10
IMG_SHAPE = (256, 256)
GREY_LEVEL = 128
MU = 0
SIGMA = 2

class Homework2(object):

    def __init__(self):
        pass

    @staticmethod
    def gen_one_image():
        """Generate one image."""
        img = GREY_LEVEL * np.ones(IMG_SHAPE)
        noise = np.random.normal(MU, SIGMA, IMG_SHAPE)
        image = img + noise

        return image

    def gen_image(self, nimage):
        """Generate all required images."""
        tmp = [0] * nimage
        for i in np.arange(nimage):
            tmp[i] = self.gen_one_image()

        return np.array(tmp)

    @staticmethod
    def EST_NOISE(images):
        """Implementation of EST_NOISE in Chapter 2 of Trucco and Verri."""
        num = images.shape[0]
        m_e_bar = sum(images)/num
        m_sigma = np.sqrt(sum((images - m_e_bar)**2)/(num - 1))

        return m_sigma

    def solve_p1(self):
        """Solve problem #1."""
        all_image = self.gen_image(NUMBER)
        result = self.EST_NOISE(all_image)
        print("\n\nSolution for problem #1.")
        self.plot_result(all_image, result)

        self.images = all_image

    @staticmethod
    def apply_2d_filter(bfilter, timage):
        """Apply given 2D filter onto an image.

        Parameters
        ----------
        bfilter: array-like
            The filter
        timage: array-like
            Targeted image
        """
        image_shape = timage.shape
        ovrlay = int(bfilter.shape[0] / 2)
        tmp_matrix = np.zeros(np.array(image_shape) + 2 * ovrlay)
        tmp_matrix[ovrlay:-ovrlay, ovrlay:-ovrlay] = timage
        res_matrix = np.zeros(timage.shape)
        for i in np.arange(image_shape[0]) + ovrlay:
            for j in np.arange(image_shape[1]) + ovrlay:
                local_matrix = tmp_matrix[i - ovrlay:i + ovrlay + 1,
                                          j - ovrlay:j + ovrlay + 1]
                res_matrix[i - ovrlay, j - ovrlay] = sum(sum(local_matrix * bfilter))
        return res_matrix

    def solve_p2(self):
        """Solve problem #2."""
        box_filter = np.ones((3, 3)) / 9
        all_image = self.images
        filted_image = [0] * NUMBER
        for i in np.arange(NUMBER):
            filted_image[i] = self.apply_2d_filter(box_filter, all_image[i])
        filted_image = np.array(filted_image)
        result = self.EST_NOISE(filted_image)
        print("\n\nSolution for problem #2.")
        self.plot_result(filted_image, result)

    @staticmethod
    def plot_result(images, res):
        """Plot results."""
        fig = plt.figure()
        for i in np.arange(NUMBER):
            ax = fig.add_subplot(2, 5, i + 1)
            ax.axis('off')
            ax.imshow(images[i], cmap=plt.cm.gray)
        fig.suptitle(f"Estimated Noise {res.mean():.4f}, Worst Case Noise {res.max():.4f}",
                     fontsize=12)
        fig.show()

    def solve_p3(self, sigma=1.4, show=True):
        """Solve problem #1."""
        n = int(2 * np.ceil(2 * sigma) + 1)
        # 2D Gaussian
        ovrlay = int(n / 2)
        inds = np.arange(-ovrlay, ovrlay + 1)
        x, y = np.meshgrid(inds, inds)
        mask = np.exp(-(x**2 + y**2)/(2*sigma**2))
        mask = mask/sum(sum(mask))

        # two 1D Gaussian
        gaussian_1d = np.exp(-inds**2/(2 * sigma**2))
        gaussian_1d = gaussian_1d /sum(gaussian_1d).reshape((-1, 1))
        mask2 = gaussian_1d * gaussian_1d.T

        print("\n\nSolution for problem #3.")

        if show:
            Fsize = 16
            test_img = mpimg.imread(r".\fig\test.png")
            output_img1 = self.apply_2d_filter(mask, test_img)
            output_img2 = self.apply_2d_filter(mask2, test_img)
            fig = plt.figure()
            ax1 = fig.add_subplot(131)
            ax1.axis('off')
            ax1.imshow(test_img, cmap=plt.cm.gray)
            ax1.set_title("Raw image", fontsize=Fsize)
            ax2 = fig.add_subplot(132, sharex=ax1, sharey=ax1)
            ax2.axis('off')
            ax2.imshow(output_img1, cmap=plt.cm.gray)
            ax2.set_title(f"2D {n}x{n} Gaussian", fontsize=Fsize)
            ax3 = fig.add_subplot(133, sharex=ax1, sharey=ax1)
            ax3.axis('off')
            ax3.imshow(output_img2, cmap=plt.cm.gray)
            ax3.set_title(f"Two 1D n={n} Gaussian", fontsize=Fsize)
            fig.show()

        return mask, gaussian_1d

    @staticmethod
    def apply_1d_filter(bfilter, timage):
        """Apply given 1D filter onto an image.

        Parameters
        ----------
        bfilter: array-like
            The filter
        timage: array-like
            Targeted image
        """
        image_length = len(timage)
        ovrlay = int(bfilter.shape[0] / 2)
        tmp_array = np.zeros(image_length + 2 * ovrlay)
        tmp_array[ovrlay:-ovrlay] = timage
        res_array = np.zeros(image_length )
        for i in np.arange(image_length) + ovrlay:
            local_matrix = tmp_array[i - ovrlay:i + ovrlay + 1]
            res_array[i - ovrlay] = sum(local_matrix * bfilter)
        return res_array

    @staticmethod
    def apply_1d_median_filter(n, timage):
        """Applying a 3 median flter on the image I assuming that the
        border pixels are not changed.

        Parameters
        ----------
        n: int
            Shape of median filter
        timage: array-like
            Targeted image
        """
        image_shape = timage.shape
        ovrlay = int(n / 2)
        res_matrix = np.copy(timage)
        for i in np.arange(image_shape[0])[1:-1]:
            local_matrix = timage[i - ovrlay:i + ovrlay + 1]
            median = np.median(local_matrix)
            res_matrix[i] = median
        return res_matrix

    def solve_p4(self):
        """Solve problem #4."""
        I = np.array([10] * 5 + [40] * 5)
        filter1 = np.ones(5)/5
        filter2 = np.array([1, 2, 4, 2, 1]) / 10

        O1 = self.apply_1d_filter(filter1, I).astype(int)
        O2 = self.apply_1d_filter(filter2, I).astype(int)
        print("\n\nSolution for problem #4.")
        print("Filter (a)")
        print(O1)
        print("Filter (b)")
        print(O2)
        return O1, O2

    @staticmethod
    def apply_2d_median_filter(n, timage):
        """Applying a nxn median filter on the image I assuming that the
        border pixels are not changed."""
        image_shape = timage.shape
        ovrlay = int(n / 2)
        res_matrix = np.copy(timage)
        for i in np.arange(image_shape[0])[1:-1]:
            for j in np.arange(image_shape[1])[1:-1]:
                local_matrix = timage[i - ovrlay:i + ovrlay + 1,
                                      j - ovrlay:j + ovrlay + 1]
                median = np.median(local_matrix)
                res_matrix[i, j] = median
        return res_matrix

    def solve_p5(self, show=True):
        """Solve problem #5."""
        print("\n\nSolution for problem #5.")
        if show:
            fig = plt.figure()
            ax1 = fig.add_subplot(121)
            ax1.stem([-100, 0, 100], np.array([49, 42, 9])/100)
            ax1.set_ylabel("Probability")
            ax1.set_xlabel("Output")
            ax1.set_title("On the line")
            ax2 = fig.add_subplot(122)
            ax2.stem([-150, -50, 50, 100], np.array([21, 9, 21, 49])/100)
            ax2.set_ylabel("Probability")
            ax2.set_xlabel("Output")
            ax2.set_title("On the adjacent line")
            fig.show()

    def solve_p6(self):
        """Solve problem #6."""
        I = np.zeros((8, 8)).astype(int)
        for i in np.arange(8):
            for j in np.arange(8):
                I[i, j] = np.abs(i - j)
        O = self.apply_2d_median_filter(3, I).astype(int)
        print("\n\nSolution for problem #6.")
        print(I)
        print(O)
        return I, O

    def solve_p7(self):
        """Solve problem #7."""
        I = np.array([4] * 4 + [8] * 4)
        O1 = self.apply_1d_median_filter(3, I)
        avgfilter = np.array([1, 2, 1]) / 4
        O2 = np.copy(I)
        O2[1:-1] = self.apply_1d_filter(avgfilter, I)[1:-1]

        print("\n\nSolution for problem #7.")
        print("Median Filter")
        print(O1)
        print("Average Mask")
        print(O2)
        return O1, O2

if __name__ == "__main__":
    hw2 = Homework2()
    hw2.solve_p1()
    hw2.solve_p2()
    hw2.solve_p3()
    hw2.solve_p4()
    hw2.solve_p5()
    hw2.solve_p6()
    hw2.solve_p7()
#


'''
This script demonstrate how to optimize the hyperparameters for a
Gaussian process based on the marginal likelihood. Optimization is
performed in two ways, first with a grid search method and then with a
downhill simplex method.
'''
import numpy as np
import matplotlib.pyplot as plt
from scipy.optimize import fmin
from rbf.gproc import gpiso
np.random.seed(3)

# True signal which we want to recover. This is a exponential function
# with mean=0.0, variance=2.0, and time-scale=0.1. For graphical
# purposes, we will only estimate the variance and time-scale.
eps = 0.1
var = 2.0
gp = gpiso('exp', eps=eps, var=var)

n = 500 # number of observations
time = np.linspace(-5.0, 5.0, n)[:,None] # observation points
data = gp.sample(time) # signal which we want to describe

# find the optimal hyperparameter with a brute force grid search
eps_search = 10**np.linspace(-2, 1, 30)
var_search = 10**np.linspace(-2, 2, 30)
log_likelihoods = np.zeros((30, 30))
for i, eps_test in enumerate(eps_search):
  for j, var_test in enumerate(var_search):
    gp = gpiso('exp', eps=eps_test, var=var_test)
    log_likelihoods[i, j] = gp.log_likelihood(time, data)

# find the optimal hyperparameters with a positively constrained
# downhill simplex method
def fmin_pos(func, x0, *args, **kwargs):
  '''fmin with positivity constraint'''
  def pos_func(x, *blargs):
    return func(np.exp(x), *blargs)

  out = fmin(pos_func, np.log(x0), *args, **kwargs)
  out = np.exp(out)
  return out

def objective(x, t, d):
  '''objective function to be minimized'''
  gp = gpiso('exp', eps=x[0], var=x[1])
  return -gp.log_likelihood(t,d)

# maximum likelihood estimate
eps_mle, var_mle = fmin_pos(objective, [1.0, 1.0], args=(time, data))

# plot the results
fig, axs = plt.subplots(2, 1, figsize=(6, 6))
ax = axs[0]
ax.grid(True)
ax.plot(time[:,0], data, 'k.', label='observations')
ax.set_xlim((-5.0, 5.0))
ax.set_ylim((-5.0, 7.0))
ax.set_xlabel('time', fontsize=10)
ax.set_ylabel('observations', fontsize=10)
ax.tick_params(labelsize=10)
ax.text(0.55, 0.85, r"$C_u(x,x') = \sigma^2\exp(-|x - x'|/\epsilon)$", transform=ax.transAxes)
ax.text(0.55, 0.775, r"$\sigma^2 = %s$, $\epsilon = %s$" % (var, eps), transform=ax.transAxes)

ax = axs[1]
ax.set_yscale('log')
ax.set_xscale('log')
ax.set_xlabel(r'$\epsilon$', fontsize=10)
ax.set_ylabel(r'$\sigma^2$', fontsize=10)
ax.tick_params(labelsize=10)
p = ax.contourf(eps_search, var_search, log_likelihoods.T, np.linspace(-5000.0, -200.0, 11), cmap='viridis')
cbar = plt.colorbar(p, ax=ax)
ax.plot([eps], [var], 'ro', markersize=10, mec='none', label='truth')
ax.plot([eps_mle], [var_mle], 'ko', markersize=10, mec='none', label='max likelihood')
ax.legend(fontsize=10, loc=2)
cbar.set_label('log likelihood', fontsize=10)
cbar.ax.tick_params(labelsize=10)
ax.grid(True)
plt.tight_layout()
plt.savefig('../figures/gproc.f.png')
plt.show()


# a simple cubic spline example.
#
# generate some random data in 10 intervals -- note the data changes
# each time this is run.
#
# Our form of the spline polynomial comes from Pang, Ch. 2
#
# in this version, we use an iterative method to solve the tridiagonal system
#
# plot the splines
#
# M. Zingale (2013-03-03)

import numpy
import pylab
import math
from scipy import linalg   # scipy modules need to be imported separately

# Jacobi tolerance
tol = 1.e-12

# plot a spline
def plot_spline(x0, x1, f0, f1, ppp0, ppp1):

    # lots of points for a smooth plot
    x = numpy.linspace(x0, x1, 100)

    dx = x1-x0

    alpha = ppp1/(6.0*dx)
    beta = -ppp0/(6.0*dx)

    gamma = (-ppp1*dx*dx/6.0 + f1)/dx
    eta = (ppp0*dx*dx/6.0 - f0)/dx

    p = alpha*(x-x0)**3 + beta*(x-x1)**3 + gamma*(x-x0) + eta*(x-x1)

    pylab.plot(x, p)

# number of intervals
n = 20

xmin = 0.0
xmax = 1.0

# coordinates of the data locations
x = numpy.linspace(xmin, xmax, n+1)
dx = x[1] - x[0]

# random data
f = numpy.random.rand(n+1)

# we are solving for n-1 unknowns

# setup the righthand side of our matrix equation
b = numpy.zeros(n+1)

# b_i = (6/dx) * (f_{i-1} - 2 f_i + f_{i+1})
# here we do this with slice notation to fill the
# inner n-1 slots of b
b[1:n] = (6.0/dx)*(f[0:n-1] - 2.0*f[1:n] + f[2:n+1])

# use Jacobi iteration to solve this system.  Note, we don't need to
# write down the matrix for this, we know that the form of the system
# we are solving is:
#
#   dx x_{i-1} + 4 dx x_i + dx_{i+1} = b_i
#
# we only solve for i = 1, ..., n-1, and we impose the boundary
# conditions: x[0] = x[n] = 0

# allocate space for the solution and old value
xold = numpy.zeros(n+1)
xsol = numpy.zeros(n+1)

# note that the BCs: x[0] = x[n] = 0 are already initialized

# iterate
err = 1.e30
iter = 0
while (err > tol):
    # xsol_i = (b_i - dx x_{i-1} - dx x_{i+1})/(4dx)
    xsol[1:n] = (b[1:n] - dx*xold[0:n-1] - dx*xold[2:n+1])/(4.0*dx)

    iter += 1
    err = numpy.max(numpy.abs((xsol[1:n] - xold[1:n])/xsol[1:n]))

    print iter, err
    xold = xsol.copy()

# for debugging, use the built-in banded solver from numpy
u = numpy.zeros(n-1)
d = numpy.zeros(n-1)
l = numpy.zeros(n-1)

d[:] = 4.0*dx
u[:] = dx; u[0] = 0.0
l[:] = dx; l[n-2] = 0.0

# create a banded matrix -- this doesn't store every element -- just
# the diagonal and one above and below and solve
A = numpy.matrix([u,d,l])
xsol_np = linalg.solve_banded((1,1), A, b[1:n])

# xsol_np now hold all the second derivatives for points 1 to n-1.
# Natural boundary conditions are imposed here
xsol_np = numpy.insert(xsol_np, 0, 0)
xsol_np = numpy.insert(xsol_np, n, 0) # insert at the end

# report error from our iterative method vs. direct solve
err = numpy.max(numpy.abs((xsol[1:n] - xsol_np[1:n])/xsol_np[1:n]))

print "relative error of iterative solution compared to direct: ", err

# go ahead with our iterative solution -- natural boundary conditions are
# already in place for this
ppp = xsol

# now plot -- data points first
pylab.scatter(x, f, marker="x", color="r")

# plot the splines
i = 0
while i < n:

    # working on interval [i,i+1]
    ppp_i = ppp[i]
    ppp_ip1 = ppp[i+1]

    f_i = f[i]
    f_ip1 = f[i+1]

    x_i = x[i]
    x_ip1 = x[i+1]

    plot_spline(x_i, x_ip1, f_i, f_ip1, ppp_i, ppp_ip1)

    i += 1

pylab.savefig("spline-iterative.png")

# note: we could have done this all through scipy -- here is their
# spline, but it doesn't seem to support natural boundary conditions

#s = interpolate.InterpolatedUnivariateSpline(x, f, k=3)
#xx = numpy.linspace(xmin, xmax, 1000)
#pylab.plot(xx, s(xx), color="k", ls=":")

# old way from scipy -- this raises a NotImplementedError for natural
#spl1 = interpolate.splmake(x, f, order=3, kind="natural")
#xx = numpy.linspace(xmin, xmax, 1000)
#yy = interpolate.spleval(spl1, xx)
#pylab.plot(xx, yy, color="k", ls=":")

#pylab.savefig("spline-scipy.png")