import numpy as np
import pandas as pd
import patsy
import warnings

def sigmoid(x):
    '''Sigmoid function of x.'''
    return 1/(1+np.exp(-x))

np.random.seed(0)

tol = 1e-8 # convergence tolerance

lam = None # l2-regularization
max_iter = 20 # maximum allowed iterations

r = 0.95 # covariance between x and z
n = 1000 # number of observations (size of dataset to generate)
sigma = 1 # variance of noise - how spread out is the data?

beta_x, beta_z, beta_v = -4, .9, 1 # true beta coefficients
var_x, var_z, var_v = 1, 1, 4 # variances of inputs

## the model specification you want to fit
formula = 'y ~ x + z + v + np.exp(x) + I(v**2 + z)'

#lets keep x and z closely related (height and weight)
x, z = np.random.multivariate_normal([0,0], [[var_x,r],[r,var_z]], n).T
#blood presure
v = np.random.normal(0,var_v,n)**3

#create a pandas dataframe (easily parseable object for manipulation)
A = pd.DataFrame({'x' : x, 'z' : z, 'v' : v})
#compute the log odds for our 3 independent variables
#using the sigmoid function
A['log_odds'] = sigmoid(A[['x','z','v']].dot([beta_x,beta_z,beta_v]) + sigma*np.random.normal(0,1,n))

A['y'] = [np.random.binomial(1,p) for p in A.log_odds]

#create a dataframe that encompasses our input data, model formula, and outputs
y, X = patsy.dmatrices(formula, A, return_type='dataframe')

#print it
print(X.head(100))

def catch_singularity(f):
    '''Silences LinAlg Errors and throws a warning instead.'''

    def silencer(*args, **kwargs):
        try:
            return f(*args, **kwargs)
        except np.linalg.LinAlgError:
            warnings.warn('Algorithm terminated - singular Hessian!')
            return args[0]

    return silencer

@catch_singularity
def newton_step(curr, X, lam=None):
    '''One naive step of Newton's Method'''

    # how to compute inverse? http://www.mathwarehouse.com/algebra/matrix/images/square-matrix/inverse-matrix.gif

    ## compute necessary objects
    # create probability matrix, miniminum 2 dimensions, tranpose (flip it)
    p = np.array(sigmoid(X.dot(curr[:, 0])), ndmin=2).T
    # create weight matrix from it
    W = np.diag((p * (1 - p))[:, 0])
    # derive the hessian
    hessian = X.T.dot(W).dot(X)
    # derive the gradient
    grad = X.T.dot(y - p)

    ## regularization step (avoiding overfitting)
    if lam:
        # Return the least-squares solution to a linear matrix equation
        step, *_ = np.linalg.lstsq(hessian + lam * np.eye(curr.shape[0]), grad)
    else:
        step, *_ = np.linalg.lstsq(hessian, grad)

    ## update our
    beta = curr + step

    return beta

@catch_singularity
def alt_newton_step(curr, X, lam=None):
    '''One naive step of Newton's Method'''

    ## compute necessary objects
    p = np.array(sigmoid(X.dot(curr[:, 0])), ndmin=2).T
    W = np.diag((p * (1 - p))[:, 0])
    hessian = X.T.dot(W).dot(X)
    grad = X.T.dot(y - p)

    ## regularization
    if lam:
        # Compute the inverse of a matrix.
        step = np.dot(np.linalg.inv(hessian + lam * np.eye(curr.shape[0])), grad)
    else:
        step = np.dot(np.linalg.inv(hessian), grad)

    ## update our weights
    beta = curr + step

    return beta

def check_coefs_convergence(beta_old, beta_new, tol, iters):
    '''Checks whether the coefficients have converged in the l-infinity norm.
    Returns True if they have converged, False otherwise.'''
    # calculate the change in the coefficients
    coef_change = np.abs(beta_old - beta_new)

    # if change hasn't reached the threshold and we have more iterations to go, keep training
    return not (np.any(coef_change > tol) & (iters < max_iter))

## initial conditions
# initial coefficients (weight values), 2 copies, we'll update one
beta_old, beta = np.ones((len(X.columns), 1)), np.zeros((len(X.columns), 1))

# num iterations we've done so far
iter_count = 0
# have we reached convergence?
coefs_converged = False

# if we haven't reached convergence... (training step)
while not coefs_converged:
    # set the old coefficients to our current
    beta_old = beta
    # perform a single step of newton's optimization on our data, set our updated beta values
    beta = newton_step(beta, X, lam=lam)
    # increment the number of iterations
    iter_count += 1

    # check for convergence between our old and new beta values
    coefs_converged = check_coefs_convergence(beta_old, beta, tol, iter_count)

print('Iterations : {}'.format(iter_count))
print('Beta : {}'.format(beta))


# -*- coding: utf-8 -*-
"""
Created on Sat May  4 18:46:21 2019
FIR (ventana Kaiser)
Tecnológico Nacional de México
Tecnológico de Estudios Superiores de Ixtapaluca
División de Ingeniería Electrónica
@author: M. en C. Rogelio Manuel Higuera Gonzalez
"""
import numpy as np
import matplotlib.pyplot as plt
from scipy import signal
from scipy.fftpack import fft, fftshift
window1 = signal.kaiser(51, beta=0)
window2 = signal.kaiser(51, beta=5)
window3 = signal.kaiser(51, beta=6)
window4 = signal.kaiser(51, beta=8.6)
window5 = signal.kaiser(51, beta=14)
A1 = fft(window1, 2048) / (len(window1)/2.0)
A2 = fft(window2, 2048) / (len(window2)/2.0)
A3 = fft(window3, 2048) / (len(window3)/2.0)
A4 = fft(window4, 2048) / (len(window4)/2.0)
A5 = fft(window5, 2048) / (len(window5)/2.0)
response1 = 20 * np.log10(np.abs(fftshift(A1 / abs(A1).max())))
response2 = 20 * np.log10(np.abs(fftshift(A2 / abs(A2).max())))
response3 = 20 * np.log10(np.abs(fftshift(A3 / abs(A3).max())))
response4 = 20 * np.log10(np.abs(fftshift(A4 / abs(A4).max())))
response5 = 20 * np.log10(np.abs(fftshift(A5 / abs(A5).max())))
freq = np.linspace(-0.5, 0.5, len(A1))
fig,axes=plt.subplots()
axes.plot(window1,label='Beta=0')
axes.plot(window2,label='Beta=5')
axes.plot(window3,label='Beta=6')
axes.plot(window4,label='Beta=8.6')
axes.plot(window5,label='Bata=14')
axes.set_ylabel('Amplitude')
axes.set_xlabel('Sample')
plt.legend(bbox_to_anchor=(0.35,0.45),loc=2,borderaxespad=0.1)
plt.grid(True)
plt.savefig('ventanak1.eps',dpi=1000,bbox_inches='tight')
fig,axes1=plt.subplots()
axes1.plot(freq,response1,label='Beta=0')
axes1.plot(freq,response2,label='Beta=5')
axes1.plot(freq,response3,label='Beta=6')
axes1.plot(freq,response4,label='Beta=8.6')
axes1.plot(freq,response5,label='Beta=14')
axes1.set_ylabel('Normalized magnitude [dB]')
axes1.set_xlabel('Normalized frequency [cycles per sample]')
axes1.set_ylim([-220,1])
plt.legend(bbox_to_anchor=(0.38,0.31),loc=2,borderaxespad=0.1)
plt.grid(True)
plt.savefig('ventanak2.eps',dpi=1000,bbox_inches='tight')


import jax.numpy as jnp
from jax import grad, jit, vmap
from jax import random

key = random.PRNGKey(0)

grad_tanh = grad(jnp.tanh)

def sigmoid(x):
    return 0.5 * (jnp.tanh(x / 2) + 1)

# Outputs probability of a label being true.
def predict(W, b, inputs):
    return sigmoid(jnp.dot(inputs, W) + b)

# Build a toy dataset.
inputs = jnp.array([[0.52, 1.12,  0.77],
                   [0.88, -1.08, 0.15],
                   [0.52, 0.06, -1.30],
                   [0.74, -2.49, 1.39]])
targets = jnp.array([True, True, False, True])

# Initialize random model coefficients
key, W_key, b_key = random.split(key, 3)
W = random.normal(W_key, (3,))
b = random.normal(b_key, ())

# Training loss is the negative log-likelihood of the training examples.
def loss(W, b):
    preds = predict(W, b, inputs)
    label_probs = preds * targets + (1 - preds) * (1 - targets)
    return -jnp.sum(jnp.log(label_probs))

def loss2(params_dict):
    preds = predict(params_dict['W'], params_dict['b'], inputs)
    label_probs = preds * targets + (1 - preds) * (1 - targets)
    return -jnp.sum(jnp.log(label_probs))

# Including tuple values
W_grad, b_grad = grad(loss, (0, 1))(W, b)
print('W_grad', W_grad)
print('b_grad', b_grad)

# Differentiating with respect to standard Python containers just works, so use tuples, lists, and dicts (and arbitrary nesting) however you like.
print(grad(loss2)({'W': W, 'b': b}))


import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
sns.set()
from scipy.stats import norm

np.random.seed(1)
NUM_TRIALS = 2000
BANDIT_MEANS = [1, 2, 3]

class Bandit:
    def __init__(self, true_mean):
        self.true_mean = true_mean

        # prior for mu is N(0,1)
        self.m = 0 # the predicted mean of the mean of X
        self.lambda_ = 1 # assumed precision (same as variance=1)

        self.tau = 1
        self.N = 0

    def pull(self):
        # draws a sample from N(mean=true_mean, precision=tau)
        return np.random.randn() / np.sqrt(self.tau) + self.true_mean

    def sample(self):
        # posterior
        return np.random.randn() / np.sqrt(self.lambda_) + self.m

    def update(self, x):
        # the number of samples N is 1 in the bernoulli case
        self.m = (self.tau * x + self.lambda_ * self.m) / ( self.tau + self.lambda_ )
        self.lambda_ += self.tau
        self.N += 1

def plot(bandits, trial):
    x = np.linspace(-3, 6, 200)
    for bandit in bandits:
        y = norm.pdf(x, bandit.m, np.sqrt(1. / bandit.lambda_))
        plt.plot(x, y, label=f"Real mean: {bandit.true_mean:.2f}, Number of plays: {bandit.N}")
    plt.title(f"Bandit distributions after {trial} trials")
    plt.legend()
    plt.show()

def run_experiment():
    bandits = [Bandit(m) for m in BANDIT_MEANS]
    sample_points = [5,10,20,50,100,200,500,1000,1500,1999]
    rewards = np.empty(NUM_TRIALS)

    for i in range(NUM_TRIALS):
        # Thompson sampling
        j = np.argmax([b.sample() for b in bandits])

        if i in sample_points:
            plot(bandits, i)

        x = bandits[j].pull()
        bandits[j].update(x)
        rewards[i] = x

    cumulative_average = np.cumsum(rewards) / (np.arange(NUM_TRIALS) + 1)

    # plot moving average ctr
    plt.plot(cumulative_average)
    for m in BANDIT_MEANS:
        plt.plot(np.ones(NUM_TRIALS)*m)
    plt.show()

    return cumulative_average

if __name__ == '__main__':
    run_experiment()


import numpy as np
import matplotlib.pyplot as plt

import numpy as np
import matplotlib.pyplot as plt

def FT(x):
    N = len(x)
    X = np.zeros(N, dtype=complex)

    for k in range(N):
        X[k] = 0.0j
        for n in range(N):
            X[k] += x[n] * np.exp(-2.0 * np.pi * 1.0j / N ) ** (k * n)

    return X

def IFT(x):
    N = len(x)
    X = np.zeros(N, dtype=complex)

    for k in range(N):
        X[k] = 0.0j
        for n in range(N):
            X[k] += x[n] * np.exp(2.0 * np.pi * 1.0j / N ) ** (k * n)

    return X/N

data = np.loadtxt("monthrg.dat")
time = data[:,0] + data[:,1]/12.0
signal = data[:,3]

signal = signal[time>1900]
time = time[time>1900]

# calculo la transformada
fft_signal = FT(signal)

# calculo la frecuencias
N=len(fft_signal)
f = np.zeros(N)
if N%2==0:
    f[:N//2] = np.arange(N//2)
    f[N//2:] = np.arange(-N//2,0)
else:
    f[:(N-1)//2+1] = np.arange((N-1)//2 + 1)
    f[(N-1)//2+1:] = np.arange(-(N-1)//2, 0)
d = (1.0/12.0) # intervalo entre medidas
f = f/(d*N)

# filtro de las frecuencias mayores que 0.2 years^{-1}
fft_signal_filter = fft_signal.copy()
fft_signal_filter[np.abs(f)>0.2] = 0.0

# calculo de la tranformada inversa
signal_filter = IFT(fft_signal_filter)

plt.figure(figsize=(8,14))

# plot de los datos
plt.subplot(4,1,1)
plt.scatter(time, signal, label='original', s=1.0)
plt.xlabel('Tiempo (yr)')
plt.ylabel('Numero manchas')
plt.grid()
plt.legend()

#plot de la transformada
plt.subplot(4,1,2)
plt.scatter(f, np.abs(fft_signal)/len(fft_signal), label='original', s=1.0)
plt.xlabel('Frecuencia (1/yr)')
plt.ylabel('Norma FFT / N')
plt.yscale('log')
plt.ylim([5E-3,1E2])
plt.grid()
plt.legend()

# plot de la transformada filtrada
plt.subplot(4,1,3)
plt.scatter(f, np.abs(fft_signal_filter)/len(fft_signal_filter), label='filtrado', s=1.0)
plt.xlabel('Frecuencia (1/yr)')
plt.ylabel('Norma FFT / N')
plt.yscale('log')
plt.grid()
plt.legend()
plt.ylim([5E-3,1E2])

# plot de la transformada inversa
plt.subplot(4,1,4)
plt.scatter(time, np.real(signal_filter), label='filtrado', s=1.0)
plt.xlabel('Tiempo (yr)')
plt.ylabel('Numero manchas')
plt.grid()
plt.legend()

plt.savefig('ejercicio18.png', bbox_inches='tight')


import matplotlib.pyplot as plt
import numpy as np
n=6 #number of array items
x=[24, 32, 40, 48, 64, 80] #example data with distance and speed of an object
y=[4, 6, 10, 12, 18, 27]
def least_square_fitting():

    xi=[]# create an empty array

    sx, sy, sxx, sxy, s, st=0, 0, 0, 0, 0, 0
    for i in range(n):
        sx +=x[i]
        sxx += x[i]*x[i]
        sy += y[i]
        sxy += x[i]*y[i]

    a = (n*sxy - sx*sy)/(n*sxx - sx*sx)# comes from y=a*x + b fit line
    b = (sxx*sy - sx*sxy)/(n*sxx - sx*sx)
    for i in range(0, n):
        s += pow((y[i] - a*x[i])-b, 2)#square sum
        st += pow(y[i] - (sx/n), 2)
    for i in range(n):
        xii=b + a*x[i]
        xi.append(xii)
    r2=(st-s)/st#r square

    print("coeff1:", a,"coeff2:", b, "square sum:",  s, "r square:", r2 )
    plt.plot(x, y, '.')
    plt.plot(x, xi, '-')
    plt.show()


""" combinatorics.py
OVERVIEW
========
This module was created to supplement Python's itertools module, filling in gaps
in two important areas of basic combinatorics:
(A) ordered and unordered m-way combinations, and
(B) generalizations of the four basic occupancy problems ('balls in boxes').

import collections, copy, itertools, math, operator
from numpy import sort
try:
   from miscellaneous import Timer
except:
   pass

# Define `fact` as a shorthand name for the `factorial` function:
fact= math.factorial

def prod(seq):
   """
   Because NumPy's `prod` function uses 32-bit integer arithmetic with silent
   handling of overflows, results are wrong if the correct answer would exceed
   the limits of a signed 32-bit integer.  When operating on a sequence of
   integers, the `prod` function that we define here uses large integer
   arithmetic and thus always gives correct results.
   """
   return reduce(operator.mul, seq)

def n_choose_m(n, m):
   """
   OVERVIEW
   This function calculates choose(n,m), defined as the number of ways in which
   one can select m of n distinct objects without regard for order, using only
   integer arithmetic.  The calculation is done as follows:
   1. If m > n-m, we replace m by n-m.
   2. We calculate the answer by evaluating
      prod(range(n-m+1,n+1)) / prod(range(2,m+1)),
   which is equivalent to
      n! / m! / (n-m)!
   NOTE
   Python can handle integers of arbitrary size, but the algorithm tends to bog
   down for very large values of n, partly because of the number of operations
   being performed and partly because of the memory requirements.  For values of
   n above about 10000, use `m_choose_n_ln` instead of `m_choose_n`.
   """

   if not isinstance(n,int) or not isinstance(m,int):
      raise TypeError('The inputs n and m must have type int.')

   if m < 0 or m > n:
      raise ValueError("m (the second argument) must be between 0 and n, "
        "inclusive.")

   if m > n-m: m= n-m

   if m == 0:
      return 1
   elif m == 1:
      return n

   # In the following statement, we force the division to be done using integer
   # arithmetic because otherwise Python attempts to convert the results from
   # `prod` into floating point numbers, which can fail for n greater than 170.
   return prod(range(n-m+1,n+1)) // prod(range(2,m+1))

def n_choose_m_ln(n, m):
   """
   OVERVIEW
   This function calculates the natural logarithm of choose(n,m), defined as the
   number of ways in which one can select m of n distinct objects without regard
   for order, using SciPy's `gammaln` function.  For large n, especially for
   n > 10000, this function is much faster than `n_choose_m` (computational and
   memory requirements are both much lower).
   NOTE
   To obtain a value for choose(n,m), apply the `exp` function to the result
   returned by this function.
   This function works for huge values of `n`, but applying `exp` to the return
   value may produce an overflow.
   """

   return gammaln(n+1) - gammaln(m+1) - gammaln(n-m+1)

def m_way_ordered_combinations(items, ks):
   """
   OVERVIEW
   This function returns a generator that produces all m-way ordered
   combinations (multinomial combinations) from the specified collection of
   items, with ks[i] items in the ith group, i= 0, 1, 2, ..., m-1, where m=
   len(ks) is the number of groups.  By 'ordered combinations', we mean that the
   relative order of equal-size groups is important.  The order of the items
   within any group is not important.  The total number of combinations
   generated is given by the multinomial coefficient formula (see below).
   INPUTS
   `items` must be (A) a list, tuple, or other iterable, or (B) a positive
   integer.  If `items` is an integer, it is replaced by `range(items)`.
   `ks` should be either a list or tuple containing non-negative integers, where
   the sum of these integers does not exceed the length of `items`.
   EXAMPLE
   Let items=[0,1,2,3,4,5] and ks=[2,2,2].  The output includes a total of 90
   combinations.  Two of these are the following:
   ((0, 1), (2, 3), (4, 5))
   ((2, 3), (0, 1), (4, 5))
   These are distinct because the order of the groups, which differs, is
   significant.
   NOTES
   The total number of combinations generated is given by the following
   multinomial coefficient:
                n!
   ----------------------------
   k_0! * k_1! * ... * k_(m-1)!
   where n is the number of items, m is the number of groups, and k_i is the
   number of items in the ith group.
   """
   if isinstance(items,int):
      items= range(items)
   elif not isinstance(items,collections.Iterable):
      raise TypeError("`items` must be a list, tuple, or other iterable.")

   if not isinstance(ks,(list,tuple)):
      raise TypeError("`ks` must be a list or tuple.")

   return _m_way_ordered_combinations(items, ks)

# end def m_way_ordered_combinations

def _m_way_ordered_combinations(items, ks):

   if len(ks) == 1:
      for c in itertools.combinations(items, ks[0]):
         yield (c,)

   else:
      for c_first in itertools.combinations(items, ks[0]):
         items_remaining= set(items) - set(c_first)
         for c_other in _m_way_ordered_combinations(items_remaining, ks[1:]):
            yield (c_first,) + c_other

# end def _m_way_ordered_combinations(items, ns)

def m_way_unordered_combinations(items, ks):
   """
   OVERVIEW
   This function returns a generator that produces all m-way unordered
   combinations from the specified collection of items, with ks[i] items in the
   ith group, i= 0, 1, 2, ..., m-1, where m= len(ks) is the number of groups.
   By 'unordered combinations', we mean that the relative order of equal-size
   groups is not important.  The order of the items within any group is also
   unimportant.
   INPUTS
   `items` must be (A) a list, tuple, or other iterable, or (B) a positive
   integer.  If `items` is an integer, it is replaced by `range(items)`.
   `ks` should be either a list or tuple containing non-negative integers, where
   the sum of these integers does not exceed the length of `items`.
   EXAMPLE
   Issue the following statement issued at the IPython prompt:
   list(m_way_unordered_combinations(6,[2,2,2]))
   The output consists of the 15 combinations listed below:
   (0, 1), (2, 3), (4, 5)
   (0, 1), (2, 4), (3, 5)
   (0, 1), (2, 5), (3, 4)
   (0, 2), (1, 3), (4, 5)
   (0, 2), (1, 4), (3, 5)
   (0, 2), (1, 5), (3, 4)
   (0, 3), (1, 2), (4, 5)
   (0, 3), (1, 4), (2, 5)
   (0, 3), (1, 5), (2, 4)
   (0, 4), (1, 2), (3, 5)
   (0, 4), (1, 3), (2, 5)
   (0, 4), (1, 5), (2, 3)
   (0, 5), (1, 2), (3, 4)
   (0, 5), (1, 3), (2, 4)
   (0, 5), (1, 4), (2, 3)
   NOTES
   When all group sizes are unequal, the total number of combinations generated
   is given by the multinomial coefficient (see above).  When two or more groups
   have equal sizes, the number of combinations is less than the multinomial
   coefficient because combinations that differ only in the relative order of
   equal-size groups are excluded.
   """
   if isinstance(items,int):
      items= range(items)
   elif not isinstance(items,collections.Iterable):
      raise TypeError("`items` must be a list, tuple, or other iterable.")

   if not isinstance(ks,(list,tuple)):
      raise TypeError("`ks` must be a list or tuple.")

   # Sort group sizes from largest to smallest:
   ks= list( sort(ks)[::-1] )

   return _m_way_unordered_combinations(items, ks)

# end def m_way_unordered_combinations

def _m_way_unordered_combinations(items, ks):
   """
   This generator function does the real work of generating unordered
   combinations.  See the doc string for the `m_way_unordered_combinations`
   above.
   """

   if not any(ks[1:]):
      for c in itertools.combinations(items, ks[0]):
         # yield (c,)
         yield (c,) + ((),) * (len(ks) - 1)

   else:
      for c_first in itertools.combinations(items, ks[0]):
         items_remaining= set(items) - set(c_first)
         for c_other in \
           _m_way_unordered_combinations(items_remaining, ks[1:]):
            if len(c_first)!=len(c_other[0]) or c_first<c_other[0]:
               yield (c_first,) + c_other

# end def _m_way_unordered_combinations(items, ns)

def unlabeled_balls_in_labeled_boxes(balls, box_sizes):
   """
   OVERVIEW
   This function returns a generator that produces all distinct distributions of
   indistinguishable balls among labeled boxes with specified box sizes
   (capacities).  This is a generalization of the most common formulation of the
   problem, where each box is sufficiently large to accommodate all of the
   balls, and is an important example of a class of combinatorics problems
   called 'weak composition' problems.
   CONSTRUCTOR INPUTS
   n: the number of balls
   box_sizes: This argument is a list of length 1 or greater.  The length of
   the list corresponds to the number of boxes.  `box_sizes[i]` is a positive
   integer that specifies the maximum capacity of the ith box.  If
   `box_sizes[i]` equals `n` (or greater), the ith box can accommodate all `n`
   balls and thus effectively has unlimited capacity.
   ACKNOWLEDGMENT
   I'd like to thank Chris Rebert for helping me to convert my prototype
   class-based code into a generator function.
   """
   if not isinstance(balls, int):
      raise TypeError("balls must be a non-negative integer.")
   if balls < 0:
      raise ValueError("balls must be a non-negative integer.")

   if not isinstance(box_sizes,list):
      raise ValueError("box_sizes must be a non-empty list.")

   capacity= 0
   for size in box_sizes:
      if not isinstance(size, int):
          raise TypeError("box_sizes must contain only positive integers.")
      if size < 1:
          raise ValueError("box_sizes must contain only positive integers.")
      capacity+= size

   if capacity < balls:
      raise ValueError("The total capacity of the boxes is less than the "
        "number of balls to be distributed.")

   return _unlabeled_balls_in_labeled_boxes(balls, box_sizes)

def _unlabeled_balls_in_labeled_boxes(balls, box_sizes):
   """
   This recursive generator function was designed to be returned by
   `unlabeled_balls_in_labeled_boxes`.
   """

   # If there are no balls, all boxes must be empty:
   if not balls:
      yield len(box_sizes) * (0,)

   elif len(box_sizes) == 1:

      # If the single available box has sufficient capacity to store the balls,
      # there is only one possible distribution, and we return it to the caller
      # via `yield`.  Otherwise, the flow of control will pass to the end of the
      # function, triggering a `StopIteration` exception.
      if box_sizes[0] >= balls:
          yield (balls,)

   else:

      # Iterate over the number of balls in the first box (from the maximum
      # possible down to zero), recursively invoking the generator to distribute
      # the remaining balls among the remaining boxes.
      for balls_in_first_box in xrange( min(balls, box_sizes[0]), -1, -1 ):
         balls_in_other_boxes= balls - balls_in_first_box

         for distribution_other in _unlabeled_balls_in_labeled_boxes(
           balls_in_other_boxes, box_sizes[1:]):
            yield (balls_in_first_box,) + distribution_other

   # end three alternative blocks

# end def _unlabeled_balls_in_labeled_boxes(balls, box_sizes)

def unlabeled_balls_in_unlabeled_boxes(balls, box_sizes):
   """
   OVERVIEW
   This function returns a generator that produces all distinct distributions of
   indistinguishable balls among indistinguishable boxes, with specified box
   sizes (capacities).  This is a generalization of the most common formulation
   of the problem, where each box is sufficiently large to accommodate all of
   the balls.  It might be asked, 'In what sense are the boxes indistinguishable
   if they have different capacities?' The answer is that the box capacities
   must be considered when distributing the balls, but once the balls have been
   distributed, the identities of the boxes no longer matter.
   CONSTRUCTOR INPUTS
   n: the number of balls
   box_sizes: This argument is a list of length 1 or greater.  The length of
   the list corresponds to the number of boxes.  `box_sizes[i]` is a positive
   integer that specifies the maximum capacity of the ith box.  If
   `box_sizes[i]` equals `n` (or greater), the ith box can accommodate all `n`
   balls and thus effectively has unlimited capacity.
   NOTE
   For `unlabeled_balls_in_unlabeled_boxes`, the order of the elements of the
   `box_sizes` list is unimportant because the code will sort it into non-
   increasing order before any other processing is done.
   """
   if not isinstance(balls, int):
      raise TypeError("balls must be a non-negative integer.")
   if balls < 0:
      raise ValueError("balls must be a non-negative integer.")

   if not isinstance(box_sizes,list):
      raise ValueError("box_sizes must be a non-empty list.")

   capacity= 0
   for size in box_sizes:
      if not isinstance(size, int):
          raise TypeError("box_sizes must contain only positive integers.")
      if size < 1:
          raise ValueError("box_sizes must contain only positive integers.")
      capacity+= size

   if capacity < balls:
      raise ValueError("The total capacity of the boxes is less than the "
        "number of balls to be distributed.")

   # Sort the box sizes so that the values decrease:
   box_sizes= list( sort(box_sizes)[::-1] )

   return _unlabeled_balls_in_unlabeled_boxes(balls, box_sizes)

# def unlabeled_balls_in_unlabeled_boxes(balls, box_sizes)

def _unlabeled_balls_in_unlabeled_boxes(balls, box_sizes):
   """
   This recursive generator function was designed to be returned by
   `unlabeled_balls_in_unlabeled_boxes`.
   """

   # If there are no balls, all boxes must be empty.
   if not balls:
      yield len(box_sizes) * (0,)

   elif len(box_sizes) == 1:

      # If the single available box has sufficient capacity to store the balls,
      # there is only one possible distribution, and we return it to the caller
      # via `yield`.  Otherwise, the flow of control will pass to the end of the
      # generator function, triggering a `StopIteration` exception.
      if box_sizes[0] >= balls:
          yield (balls,)

   else:

      # Iterate over the number of balls in the first box (from the maximum
      # possible down to zero), recursively invoking the generator to distribute
      # the remaining balls among the remaining boxes.
      for balls_in_first_box in xrange( min(balls, box_sizes[0]), -1, -1 ):
         balls_in_other_boxes= balls - balls_in_first_box

         for distribution_other in _unlabeled_balls_in_unlabeled_boxes(
           balls_in_other_boxes, box_sizes[1:]):

            # To prevent the possibility of duplicating a distribution that has
            # been obtained previously, we require that the number of balls in
            # the second box (first of the 'other' boxes) not exceed the number
            # of balls in the first box:
            if distribution_other[0] <= balls_in_first_box:
               yield (balls_in_first_box,) + distribution_other

   # end three alternative blocks

# def _unlabeled_balls_in_unlabeled_boxes(balls, box_sizes)

def labeled_balls_in_unlabeled_boxes(balls, box_sizes):
   """
   OVERVIEW
   This function returns a generator that produces all distinct distributions of
   distinguishable balls among indistinguishable boxes, with specified box sizes
   (capacities).  This is a generalization of the most common formulation of the
   problem, where each box is sufficiently large to accommodate all of the
   balls.  It might be asked, 'In what sense are the boxes indistinguishable if
   they have different capacities?'  The answer is that the box capacities must
   be considered when distributing the balls, but once the balls have been
   distributed, the identities of the boxes no longer matter.
   CONSTRUCTOR INPUTS
   n: the number of balls
   box_sizes: This argument is a list of length 1 or greater.  The length of
   the list corresponds to the number of boxes.  `box_sizes[i]` is a positive
   integer that specifies the maximum capacity of the ith box.  If
   `box_sizes[i]` equals `n` (or greater), the ith box can accommodate all `n`
   balls and thus effectively has unlimited capacity.
   NOTE
   For `labeled_balls_in_unlabeled_boxes`, the order of the elements of the
   `box_sizes` list is unimportant because the code will sort it into non-
   increasing order before any other processing is done.
   """
   if not isinstance(balls, int):
      raise TypeError("balls must be a non-negative integer.")
   if balls < 0:
      raise ValueError("balls must be a non-negative integer.")

   if not isinstance(box_sizes,list):
      raise ValueError("box_sizes must be a non-empty list.")

   capacity= 0
   for size in box_sizes:
      if not isinstance(size, int):
          raise TypeError("box_sizes must contain only positive integers.")
      if size < 1:
          raise ValueError("box_sizes must contain only positive integers.")
      capacity+= size

   if capacity < balls:
      raise ValueError("The total capacity of the boxes is less than the "
        "number of balls to be distributed.")

   for unlabeled_dist in unlabeled_balls_in_unlabeled_boxes(balls, box_sizes):
      for labeled_dist in \
        m_way_unordered_combinations(balls, unlabeled_dist):
         yield labeled_dist

# end def labeled_balls_in_unlabeled_boxes(balls, box_sizes)

def labeled_balls_in_labeled_boxes(balls, box_sizes):
   """
   OVERVIEW
   This function returns a generator that produces all distinct distributions of
   distinguishable balls among distinguishable boxes, with specified box sizes
   (capacities).  This is a generalization of the most common formulation of the
   problem, where each box is sufficiently large to accommodate all of the
   balls.
   CONSTRUCTOR INPUTS
   n: the number of balls
   box_sizes: This argument is a list of length 1 or greater.  The length of
   the list corresponds to the number of boxes.  `box_sizes[i]` is a positive
   integer that specifies the maximum capacity of the ith box.  If
   `box_sizes[i]` equals `n` (or greater), the ith box can accommodate all `n`
   balls and thus effectively has unlimited capacity.
   EXAMPLE
   Issue the following statement issued at the IPython prompt:
   list(labeled_balls_in_labeled_boxes(3,[2,2]))
   The output is as follows:
   [((0, 1), (2,)),
    ((0, 2), (1,)),
    ((1, 2), (0,)),
    ((0,), (1, 2)),
    ((1,), (0, 2)),
    ((2,), (0, 1))]
   """
   if not isinstance(balls, int):
      raise TypeError("balls must be a non-negative integer.")
   if balls < 0:
      raise ValueError("balls must be a non-negative integer.")

   if not isinstance(box_sizes,list):
      raise ValueError("box_sizes must be a non-empty list.")

   capacity= 0
   for size in box_sizes:
      if not isinstance(size, int):
          raise TypeError("box_sizes must contain only positive integers.")
      if size < 1:
          raise ValueError("box_sizes must contain only positive integers.")
      capacity+= size

   if capacity < balls:
      raise ValueError("The total capacity of the boxes is less than the "
        "number of balls to be distributed.")

   for unlabeled_dist in unlabeled_balls_in_labeled_boxes(balls, box_sizes):
      for labeled_dist in m_way_ordered_combinations(balls, unlabeled_dist):
         yield labeled_dist

# end def labeled_balls_in_labeled_boxes(balls, box_sizes)

def partitions(n):
   """
   'In number theory and combinatorics, a partition of a positive integer n,
   also called an integer partition, is a way of writing n as a sum of positive
   integers.  Two sums that differ only in the order of their summands are
   considered to be the same partition.'  We can trivially generate all
   partitions of an integer using `unlabeled_balls_in_unlabeled_boxes`.  The
   quote is from http://en.wikipedia.org/wiki/Partition_(number_theory) .
   """

   _partitions= unlabeled_balls_in_unlabeled_boxes(n, n*[n])

   for _partition in _partitions:
      yield tuple([p for p in _partition if p])

def partitions2(n):
   """
   This function generates integer partitions; it was written by David Eppstein.
   """
   # base case of recursion: zero is the sum of the empty list
   if n == 0:
      yield []
      return

   # modify partitions of n-1 to form partitions of n
   for p in partitions2(n-1):
      yield [1] + p
      if p and (len(p) < 2 or p[1] > p[0]):
         yield [p[0] + 1] + p[1:]

def off_by_one(n):
   """
   OVERVIEW
   This function returns a generator that enumerates all possible solutions of
   the 'off-by-one' problem.  This problem can be stated as follows:
      A list of n items is provided.  Each item is in its correct position, or
      one before or one after its correct position.  Enumerate all possibilities
      for the correct ordering.
   EXAMPLE
   With two lines of code, one can generate a table showing the number of
   possible orders versus n:
     for i in range(2,10):
        print('%d, %d' % (i, len(list(off_by_one(i))))
   The output is as follows:
     2, 2
     3, 3
     4, 5
     5, 8
     6, 13
     7, 21
     8, 34
     9, 55
   Note that the number of possible orders follows the Fibonacci sequence.
   """

   Seq= range(n)
   return _off_by_one(Seq)

def _off_by_one(Seq):
   """
   This recursive generator function was designed to be returned by
   `off_by_one`.
   """

   if len(Seq) <= 1:
      yield Seq

   else:

      # There are two possibilities:

      # (1) The initial item of the list is in the correct position.
      Seq2= _off_by_one(Seq[1:])
      for seq2 in Seq2:
         yield [Seq[0]] + seq2

      # (2) The initial item must be switched with the item to the right of it.
      Seq2= _off_by_one(Seq[2:])
      for seq2 in Seq2:
         yield [Seq[1], Seq[0]] + seq2

def off_by_m_algorithm1(n, m):
   """
   OVERVIEW
   This generator function enumerates all possible solutions of the 'off-by-m'
   problem.  This problem can be stated as follows:
      A list of n items is provided.  Each item is in its correct position, or
      up to m positions before or after its correct position.  Enumerate all
      possibilities for the correct ordering.
   """

   # 1. Build up list of lists of possible positions for the elements of the
   # original sequence:
   allowed_positions= []

   for i in range(n):
      allowed_positions.append( range( max(i-m,0), min(i+m+1,n)) )

   # 2. Iterate over all possible configurations, yielding all legal ones.
   for config in itertools.product(*allowed_positions):
      if len(set(config)) < n:
         continue
      yield config

def off_by_m_algorithm2(n, m):
   """
   This solution for the 'off-by-m' problem was contributed by Warren Weckesser
   of Enthought.
   """

   positions = allowed_positions(n, m)
   permutation = []
   return _off_by_m_aux_gen(n, m, positions, permutation)

def allowed_positions(n, m):
   positions = []
   for k in range(n):
      low = max(-k, -m)
      high = min(n - k - 1, m)
      positions.append(range(k + low, k + high + 1))
   return positions

def _off_by_m_aux_gen(n, m, positions, permutation):
   if len(positions) == 0:
       yield permutation
   else:
       for col in positions[0]:
           pos= copy.deepcopy(positions[1:])
           for j in range(min(len(pos), 2 * m)):
               if col in pos[j]:
                   pos[j].remove(col)
           for p in _off_by_m_aux_gen(n, m, pos, permutation + [col]):
               yield p

def off_by_m_algorithm3(n, m, seq=None, i=0):
   """
   OVERVIEW
   This recursive generator function enumerates all possible solutions of the
   'off-by-m' problem.  This problem can be stated as follows:
      A list of n items is provided.  Each item is in its correct position, or
      up to m positions before or after its correct position.  Enumerate all
      possibilities for the correct ordering.
   """

   if i == 0:
      seq= n * [None]

   for j in range( max(i-m,0), min(i+m+1,n)):
      if seq[j] is not None:
         continue

      seq[j]= i

      if i >= n-1:
         yield copy.deepcopy(seq)
      else:
         for permutation in off_by_m(n, m, seq, i=i+1):
            yield permutation

      seq[j]= None

# Algorithm #3 is the fastest:
off_by_m= off_by_m_algorithm3

def off_by_m_test(n=12, m=2):
   """
   This function compares the run-time performances of three algorithms for the
   off-by-m problem.  Sample input and output appears below.
   off_by_m_test(12,2)
   Time for algorithm #1: 44.259 s
   Time for algorithm #2: 0.873 s
   Time for algorithm #3: 0.094 s
   """

   for algorithm in range(1,4):
      t= Timer()
      eval('len(list(off_by_m_algorithm%d(%d,%d)))' % (algorithm, n, m))
      print("Time for algorithm #%d: %s" % (algorithm, t.time()))


from __future__ import absolute_import, division, print_function

import numpy as np
from ..core import asoperator
from ..linear import EigendecompositionOperator, TridiagonalOperator
from .core import IterativeAlgorithm
from .stopconditions import MaxIterationStopCondition

class LanczosAlgorithm(IterativeAlgorithm):
    """
    Tridiagonalization Lanczos step and eigendecomposition at exit.

    http://en.wikipedia.org/wiki/Lanczos_algorithm
    """
    def __init__(self, A, v0=None, maxiter=300):
        """
        Use Lanczos algorithm to approximate a linear Operator.

        Parameters
        ----------
        A: Operator
            The Operator to be approximated.
        maxiter: int or None (defaults 300)
            Number of iteration (equals number of eigenvalues).
            If set to None, stops at A.shape[0]

        Returns
        -------
        A LanczosAlgorithm instance. To get the approximated Operator,
        calling this instance is required.

        Notes
        -----
        Starting point is a normalized random vector so results may
        differ from one call to another with the same input parameters.

        The Operator approximation is returned as a
        EigendecompositionOperator which can be easily inverted.
        """
        self.A = asoperator(A)
        self.n = self.A.shape[0]
        self.maxiter = maxiter
        self.norm = lambda x: np.sqrt(np.dot(x, x)) #XXX //ise me
        stop_condition = MaxIterationStopCondition(maxiter)

        IterativeAlgorithm.__init__(self, normal_stop_condition=stop_condition)
        self.v0 = v0
        # tridiagonal matrix coefficients
        self.alpha = np.zeros(self.maxiter)
        self.beta = np.zeros(self.maxiter)
        self.vectors = np.zeros((self.maxiter+1, self.n))

    def initialize(self):
        IterativeAlgorithm.initialize(self)
        if self.v0 is None:
            v0 = np.random.randn(self.n)
        else:
            v0 = self.v0.ravel()
        v0 /= self.norm(v0)
        self.vectors[0] = v0
        self.alpha[...] = 0
        self.beta[...] = 0

    def iteration(self):
        n = self.niterations
        v = self.vectors[n-1]
        v_new = self.vectors[n]
        self.A.matvec(v, out=v_new)

        # orthogonalisation
        if n > 1:
            v_new -= self.beta[n-2] * self.vectors[n-2]

        alpha = np.dot(v_new, v)
        v_new -= alpha * v
        beta = self.norm(v_new)
        v_new /= beta

        # update
        self.alpha[n-1] = alpha
        self.beta[n-1] = beta

    def finalize(self):
        """
        Convert alpha and beta to a TridiagonalOperator and perform
        eigendecomposition.

        """
        T = TridiagonalOperator(self.alpha, self.beta[:-1])
        # use band matrix eigendecomposition as LAPACK's SEV routines (?STEV*)
        # for symmetric tridiagonal matrices are not available in scipy 0.10
        E = T.toband().eigen()

        # multiply T eigenvectors with lanczos vectors
        w = E.eigenvalues
        v = np.dot(self.vectors[:-1, :].T, E.eigenvectors)

        return EigendecompositionOperator(v=v, w=w)


##! /usr/bin/env python
# -*- coding:utf-8
# Custom model creation, squared error, coefficient of determination, data creation and plotting
# Special thanks: Harisson@pythonprogramming.net

'''
    File name: cust_regress.py
    Author: chimney37
    Date created: 8/11/2017
    Python Version: 3.62
'''

import numpy as np
import matplotlib.pyplot as plt
import random
from statistics import mean
from matplotlib import style

style.use('ggplot')

xs = np.array([1, 2, 3, 4, 5], dtype=np.float64)
ys = np.array([5, 4, 6, 5, 6], dtype=np.float64)

# regression model
def best_fit_slope_and_intercept(xs, ys):
    m = (((mean(xs)*mean(ys)) - mean(xs*ys)) /
         ((mean(xs)*mean(xs)) - mean(xs*xs)))
    b = mean(ys) - m*mean(xs)
    return m, b

# determine squared error
def squared_error(ys_orig, ys_line):
    return sum((ys_line - ys_orig)*(ys_line - ys_orig))

# coefficient of determination (R squared)
def coefficient_of_determination(ys_orig, ys_line):
    y_mean_line = [mean(ys_orig) for y in ys_orig]
    squared_error_regr = squared_error(ys_orig, ys_line)
    squared_error_y_mean = squared_error(ys_orig, y_mean_line)
    return 1 - (squared_error_regr/squared_error_y_mean)

# creating dataset
def create_dataset(hm, variance, step=2, correlation=False):
    """create_dataset

    :param hm: how much = how big is data set
    :param variance: range of values (postitive and negative, centered from an incremental value val
    :param step: step is added for every increment in val
    :param correlation: 'pos' or 'neg' (positive or negative)
    """
    val = 1
    ys = []
    for i in range(hm):
        y = val + random.randrange(-variance, variance)
        ys.append(y)
        if correlation and correlation == 'pos':
            val += step
        elif correlation and correlation == 'neg':
            val -= step

    xs = [i for i in range(len(ys))]
    return np.array(xs, dtype=np.float64), np.array(ys, dtype=np.float64)

# create a data set
xs, ys = create_dataset(40, 10, 2, correlation='neg')

m, b = best_fit_slope_and_intercept(xs, ys)
print('best slope coeffcient=', m, 'best intercept=', b)

regression_line = [(m*x)+b for x in xs]
# plot a function based on the prediction of model
r_squared = coefficient_of_determination(ys, regression_line)
print('given line variables, coeffcient of determination=', r_squared)

plt.scatter(xs, ys, color='#003F72', label='data')
plt.plot(xs, regression_line, label='regression line')
plt.legend(loc=4)
plt.show()


# Function x^2
import matplotlib.pyplot as plt

import math
import numpy as np

def calculateDerivative(x):
    return 2*x

def isUnderLimit(limit, value):
    return (value > (-limit) and value < limit)

def gradientDescent(learning_rate,currentvalue,limit, maxiteration = 100):
    xcurrent = currentvalue
    xnext = xcurrent
    derivative = calculateDerivative(xcurrent)
    iteration = 0
    errors = [np.abs(xcurrent)]
    iterations = [iteration]
    while(iteration < maxiteration):
        iteration = iteration + 1
        xcurrent = xnext
        xnext = xcurrent - learning_rate * derivative
        errors.append(np.abs(xnext))
        iterations.append(iteration)
        derivative = calculateDerivative(xnext)
        if(isUnderLimit(limit,derivative)):
            print('w:::', xnext)
            break
    print('iteration:::', iteration)
    return errors, iterations

errors, iterations = gradientDescent(0.1,-2, 1e-8)
plt.plot(iterations,errors, label= "Covergence Learning Rate = 0.1",linestyle='solid')
plt.title("Error Vs Iteration")
plt.xlabel('Iteration')
plt.ylabel('Loss')
plt.legend()
plt.show()

errors1, iterations1 = gradientDescent(0.2,-2, 1e-8)
errors2, iterations2 = gradientDescent(1,-2, 1e-8)
errors3, iterations3 = gradientDescent(1.01,-2, 1e-8)
#
plt.plot(iterations1,errors1, label= "learning rate => 0.2 = Convergence",linestyle='solid')
plt.plot(iterations2,errors2, label= "learning rate => 1 = Oscillation",linestyle='solid')
plt.plot(iterations3,errors3, label= "learning rate => 1.01 = Divergence",linestyle='solid')
plt.xlabel('Iterations')
plt.ylabel('Loss')
plt.title("Error Vs Iteration for starting x = -2")
plt.legend()
plt.show()

# def ourfunction(x):
#     return x**2
# yvalues = []
# for x in range(len(xvalues)):
#     yvalues.append(ourfunction(x))
# plt.scatter(xvalues, yvalues , label= "classA", color= "green", s=10)
# x = np.linspace(-100,100,1000)
# y = x**2
# plt.plot(x,y)


import math
import random
import numpy

import qConstants as qc
import qBitStrings as qb

def equal(a, b, epsilon):
    '''Assumes that n >= 0. Assumes that a and b are both n-qbit states or n-qbit gates. Assumes that epsilon is a positive (but usually small) real number. Returns whether a == b to within a tolerance of epsilon. Useful for doing equality comparisons in the floating-point context. Warning: Does not consider global phase changes; for example, two states that are global phase changes of each other may be judged unequal. Warning: Use this function sparingly, for inspecting output and running tests. Probably you should not use it to make a crucial decision in the middle of a big computation. In past versions of CS 358, this function has not existed. I have added it this time just to streamline the tests.'''
    diff = a - b
    if len(diff.shape) == 0:
        # n == 0. Whether they're gates or states, a and b are scalars.
        return abs(diff) < epsilon
    elif len(diff.shape) == 1:
        # a and b are states.
        return sum(abs(diff)) < epsilon
    else:
        # a and b are gates.
        return sum(sum(abs(diff))) < epsilon

def uniform(n):
    '''Assumes n >= 0. Returns a uniformly random n-qbit state.'''
    if n == 0:
        return qc.one
    else:
        psiNormSq = 0
        while psiNormSq == 0:
            reals = numpy.array(
                [random.normalvariate(0, 1) for i in range(2**n)])
            imags = numpy.array(
                [random.normalvariate(0, 1) for i in range(2**n)])
            psi = numpy.array([reals[i] + imags[i] * 1j for i in range(2**n)])
            psiNormSq = numpy.dot(numpy.conj(psi), psi).real
        psiNorm = math.sqrt(psiNormSq)
        return psi / psiNorm

def bitValue(state):
    '''Given a one-qbit state assumed to be exactly classical --- usually because a classical state was just explicitly assigned to it --- returns the corresponding bit value 0 or 1.'''
    if (state == qc.ket0).all():
        return 0
    else:
        return 1

def powerMod(k, l, m):
    '''Given non-negative integer k, non-negative integer l, and positive integer m. Computes k^l mod m. Returns an integer in {0, ..., m - 1}.'''
    kToTheL = 1
    curr = k
    while l >= 1:
        if l % 2 == 1:
            kToTheL = (kToTheL * curr) % m
        l = l // 2
        curr = (curr * curr) % m
    return kToTheL

def quantumFromClassic(bitstring):
    n = len(bitstring)

    arr = numpy.array((0,) * (2 ** n))

    one_position = qb.integer(bitstring)

    arr[one_position] = 1

    return arr

def quantumListToClassicTuple(quantum_list):
    arr = []
    for qbit in quantum_list:
        arr.append(bitValue(qbit))

    return tuple(arr)

def removeZeroRow(arr):
    width = len(arr[0])
    zero_row = (0,) * width

    copy = list(arr)
    if copy[-1] == zero_row:
        copy.pop()

    return copy

def missingLeadingRow(arr):
    width = len(arr[0])
    one_row = [1,] * width

    copy = list(arr)
    for row in arr:
        first_one_index = row.index(1)
        one_row[first_one_index] = 0

    return one_row

def continuedFraction(n, m, x0):
    '''x0 is a float in [0, 1). Tries probing depths j = 0, 1, 2, ... until
    the resulting rational approximation x0 ~ c / d satisfies either d >= m or
    |x0 - c / d| <= 1 / 2^(n + 1). Returns a pair (c, d) with gcd(c, d) = 1.'''
    j = 0
    while True:
        c, d = fraction(x0, j)

        if d >= m or abs(x0 - (c / d)) <= (1 / 2 ** (n + 1)):
            return (c, d)

        j += 1

def fraction(x0, j):
    '''recursive helper to continuedFraction
    calculates the c and d out j times to approxiate x0 as c / d'''

    if x0 == 0:
        return (0, 1)
    elif j == 0:
        a0 = math.floor(1 / x0)
        c = 1
        d = a0
        return (c, d)
    else:
        a0 = math.floor(1 / x0)
        x1 = (1 / x0) - a0
        next_c, next_d = fraction(x1, j - 1)

        c = next_d
        d = (a0 * next_d) + next_c

        return (c, d)

def fractions_test():
    #test fraction
    print(fraction(1 / math.pi, 2))
    print(fraction(1 / 7, 4))
    print(fraction(0, 3))

    print(continuedFraction( 8, 4, (1 / math.pi)))

if __name__ == "__main__":
    fractions_test()


import numpy as np
import util

def as_one_hot(ind, n):
    """Turn ind into onehot."""
    vec = np.zeros(n)
    vec[ind] = 1
    return vec

def step(state_ind, transmat, atol=1e-7):
    """Take a random step in the task specified by transition matrix."""
    pvals = np.round(transmat[state_ind] / atol) * atol
    pvals = pvals / np.sum(pvals)
    next_state_onehot = np.random.multinomial(1, pvals=pvals)
    next_state = np.where(next_state_onehot)[0][0]
    return next_state

def value_update(reward, value, current_state, next_state, learning_rate, discount, features=None):
    """Update a value function in the supplied feature space.

    Args:
        reward: scalar reward received at current state
        value: value vector of len n_state
        current_state: state index for next state
        next_state: state index for next state
        learning_rate: scalar learning rate
        discount: scalar discount factor
        features: feature representation of each state

    Returns:
        value: sum of expected future reward from each state
        value_weights: weights of value function in feature space
    """
    n = len(value)
    value_shape = value.shape
    value = value.reshape(-1, 1)
    if features is None:
        features = np.eye(n)
    features_pinv = np.linalg.pinv(features)

    value_weights = np.dot(features_pinv, value)

    # prediction error
    # expected = value[current_state]
    # actual = reward + discount * value[next_state]
    expected = np.dot(features[current_state].reshape(1, -1), value_weights)
    actual = reward + discount * np.dot(features[next_state].reshape(1, -1), value_weights)
    pred_err = actual - expected

    feature_scale = features[current_state].reshape(-1, 1)
    value_weights += learning_rate * pred_err * feature_scale
    value = np.dot(features, value_weights)
    value = value.reshape(*value_shape)
    return value, value_weights

def reward_amt(value, reward_vec, adj, softmax_inv_temp, discount, start_prob=None):
    """Estimate discounted amount reward received given softmax policy over value.

    A way to assess how well your value function is doing.

    Args:
        value: value vector of len n_state
        reward_vec: reward vector of len n_state
        adj: adjacency matrix
        softmax_inv_temp: inverse temperature parameter for softmax policy
        discount: discount factor
        start_prob: probability of starting in each state (len n_state), default
            None (evaluates to uniform)

    Returns:
        sum of expected future reward from each state
    """
    n = len(reward_vec)
    softmax_value = np.exp(softmax_inv_temp *  value) / np.sum(np.exp(softmax_inv_temp*value))
    policy = adj * softmax_value.reshape(1, -1)
    policy = util.l1_normalize_rows(policy)
    sr = np.linalg.pinv(np.eye(n) - discount * policy)
    value = np.dot(sr, reward_vec.reshape(-1, 1)).reshape(-1)
    if start_prob is None:
        start_prob = np.ones(n)*1. / n
    else:
        start_prob = start_prob.reshape(n)
    return np.sum(value * start_prob)


# Exercise 5.28
# Author: Noah Waterfield Price

import numpy as np
import matplotlib.pyplot as plt
from scitools.std import movie

def animate_series(fk, M, N, xmin, xmax, ymin, ymax, n, exact, exactname):
    x = np.linspace(xmin, xmax, n)
    s = np.zeros(len(x))
    counter = 1
    for k in range(M, N + 1):
        s += fk(x, k)
        plt.plot(x, s, linewidth=2, color='#67001f')
        plt.hold(1)
        plt.plot(x, exact(x), '--', linewidth=2, color='#053061')
        plt.hold(0)
        plt.xlim(xmin, xmax)
        plt.ylim(ymin, ymax)
        plt.legend(['Taylor series approximation - %d terms' %
                   counter, exactname])
        plt.savefig('tmp_%04d.png' % counter)
        counter += 1

def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n - 1)

def f1(x, k):
    return (-1) ** k * x ** (2 * k + 1) / factorial(2 * k + 1)

def f2(x, k):
    return (-x) ** k / factorial(k)

def exp(x):
    return np.exp(-x)

#animate_series(f1, 0, 40, 0, 13*np.pi, -2, 2, 500, np.sin, 'sin(x)')
animate_series(f2, 0, 30, 0, 15, -0.5, 1.4, 500, exp, 'exp(-x)')
movie('tmp_*.png', encoder='convert', fps=3)

import glob
import os
# Remove old plot files
for filename in glob.glob('tmp_*.png'):
    os.remove(filename)


# coding=utf-8
#Monte Carlo integration with importance sampling
#Translated to Python by Kyrre Ness Sjøbæk

import numpy,math
import sys

def integrand(x):
    """Calculates the integrand
    exp(-b*[(x1-x4)^2+...+(x3-x6)^2])
    from the values in the 6-dimensional array x."""
    a = 1.0
    b = 0.5

    xy = (x[0]-x[3])**2 + (x[1]-x[4])**2 + (x[2]-x[5])**2
    return numpy.exp(-b*xy)

#Main program

#Integration limits: x[i] in (-5, 5)
L      = 5.0
jacobi = math.acos(-1.0)**3
sqrt2  = 1.0/math.sqrt(2)

if len(sys.argv) == 2:
    N = int(sys.argv[1])
else:
    print "Usage: python",sys.argv[0],"number of samples"
    sys.exit(0)

print "Running with N = %d..." % N

#Evaluate the integral
#See Numerical Recipes 7.6
sum  = 0.0
sum2 = 0.0
for i in xrange(N):
    #Generate random, gaussian distributed coordinates to sample at
    x = numpy.array([numpy.random.normal()*sqrt2 for j in xrange(6)])

    fx         = integrand(x)
    sum       += fx
    sum2      += fx**2
#Calculate expt. values for fx and fx^2
sum /=float(N)
sum2/=float(N)

#Result
int_mc  = jacobi*sum;
#Gaussian standard deviation
sigma   = jacobi*math.sqrt((sum2-sum**2)/float(N))

#Output
print "Montecarlo result = %10.8E" % int_mc
print "Sigma             = %10.8E" % sigma


import numpy as np
import scipy.optimize as opt
from numpy import sqrt, sin, cos, exp

def magnitude_phase(Mx, My):
    return {
        'x': Mx,
        'y': My,
        'magnitude': sqrt(Mx**2 + My**2),
        'phase': np.arctan2(My, Mx)
    }

def spgr(alpha, tr, t1, m0=1.0):
    e1 = exp(-tr/t1)
    return m0*(1.0-e1)*sin(alpha)/(1.0-e1*cos(alpha));

def ernst_spgr(tr, t1, m0=1.0):
    alpha = np.arccos(exp(-tr/t1))
    return {'alpha': alpha, 'spgr': spgr(alpha, tr, t1, m0)}

def ssfp_after(alpha, phase_rf, tr, t1, t2, m0=1.0, off_resonance_phase=0.0):
    """
    After RF excitation.  Lauzon 2009, Freeman 1971.
    """
    e1 = exp(-tr/t1)
    e2 = exp(-tr/t2)
    a = alpha
    b = phase_rf + off_resonance_phase
    My = m0*(1.0-e1)*sin(a)*(1.0-e2*cos(b))/((1.0-e1*cos(a))*(1.0-e2*cos(b))-e2*(e1-cos(a))*(e2-cos(b)));
    Mx = m0*(1.0-e1)*e2*sin(a)*sin(b)/((1.0-e1*cos(a))*(1.0-e2*cos(b))-e2*(e1-cos(a))*(e2-cos(b)));
    return magnitude_phase(Mx, My)

def ssfp_before(alpha, phase_rf, tr, t1, t2, m0=1.0, off_resonance_phase=0.0):
    """
    Before RF excitation.  Deoni 2009, Freeman 1971.
    """
    e1 = exp(-tr/t1)
    e2 = exp(-tr/t2)
    a = alpha
    b = phase_rf + off_resonance_phase
    My = m0*(1.0-e1)*e2*sin(a)*(cos(b)-e2)/((1.0-e1*cos(a))*(1.0-e2*cos(b))-e2*(e1-cos(a))*(e2-cos(b)));
    Mx = m0*(1.0-e1)*e2*sin(a)*sin(b)/((1.0-e1*cos(a))*(1.0-e2*cos(b))-e2*(e1-cos(a))*(e2-cos(b)));
    return magnitude_phase(Mx, My)

def ernst_ssfp(tr, t1, t2, m0=1.0):
    """
    The analytic expression for the maximum of the bSSFP curve (which is invariant to phase-cycling).
    Derived symbolically in MATLAB, Jul 2013.
    """
    phase_rf = np.pi
    e1 = exp(-tr/t1)
    e2 = exp(-tr/t2)
    alpha = np.arccos((e1-e2)/(1-e1*e2))
    signal_after = ssfp_after(alpha, phase_rf, tr, t1, t2, m0)
    signal_before = ssfp_before(alpha, phase_rf, tr, t1, t2, m0)
    return {'alpha': alpha, 'ssfp_after': signal_after, 'ssfp_before': signal_before}

def despot1_optimal_angles(tr, t1, f=2.**(-0.5)):
    """
    Solve for the optimal DESPOT1/VFA angles with f, the fraction of the max signal, =1/sqrt(2) by default.
    ref. Deoni et al. MRM 2003
    """
    e1 = exp(-tr/t1)
    f2 = f**2.
    num = f2*e1 + (1. - e1**2.) * sqrt(1. - f2) * np.array([1., -1.])
    den = 1. - e1**2. * (1. - f2)
    return np.arccos(num/den)

def despot2_optimal_angles(tr, t1, t2, f=2.**(-0.5)):
    """
    Solve for the optimal DESPOT2 angles with f, the fraction of the max signal, =1/sqrt(2) by default.
    The underlying sequence is assumed to be bSSFP with pi phase cycling
    ref. Deoni et al. MRM 2003
    """
    e1 = exp(-tr/t1)
    e2 = exp(-tr/t2)
    f2 = f**2.
    psi = (e1 - e2) / (1. - e1*e2)
    A = -f2 * (1.-psi**2.) * (e1-e2)**2. - (1. - e1*e2 - (e1-e2)*psi)**2.
    B = 2.*f2*(1.-psi**2.)*(e1-e2) - 2.*f2*(1.-psi**2.)*e1*e2*(e1-e2)
    C = (1. - e1*e2 - (e1-e2)*psi)**2. - f2*(1.-psi**2.)*(1. - 2.*e1*e2 + e1**2.*e2**2.)
    return np.arccos((-B - sqrt(B**2. - 4*A*C) * np.array([1., -1.])) / (2.*A))

def despot2_optimal_angles_numerical(phase_rf, tr, t1, t2, f=2.**(-0.5)):
    """
    Numerically solve for the optimal DESPOT2 angles with f, the fraction of the max signal, =1/sqrt(2) by default.
    ref. Deoni et al. MRM 2003
    """
    # The max signal is invariant with phase cycle.
    max_signal = ernst_ssfp(tr, t1, t2)
    offset = f*max_signal['ssfp_after']['magnitude']

    def offset_ssfp(alpha, offset=offset):
        return ssfp_after(alpha, phase_rf, tr, t1, t2)['magnitude'] - offset

    if phase_rf == np.pi:
        alpha_max = max_signal['alpha']
    else:
        alpha_max = opt.minimize(lambda alpha: -offset_ssfp(alpha), np.pi/4.)
        # Sometimes it finds the negative alpha solution, but it's symmetric
        alpha_max = np.abs(alpha_max.x)
    a = opt.brentq(offset_ssfp, 0., alpha_max)
    b = opt.brentq(offset_ssfp, alpha_max, np.pi)
    return np.array([a, b])


# Module name: Pyvect
# Short description: Pyvect is a open source python module created for the purpose of simplifying the vector calculations such as finding the angle between vectors, projection of one vector over the other and much more...!
# Developers: Vigneshwar Ravichandar(@ToastCoder), Moulishankar M R (@Moulishankar10), Vishal Balaji Sivaraman (@The-SocialLion)
# Contact email address: vicky.pcbasic@gmail.com, moulishankarmr@gmail.com, vishalsivaraman5@gmail.com
# Modules required: numpy

# Command to install pyvect:
# >>> pip install pyvect

# Essential modules
import numpy as np

# Importing Submodules
from . import area
from .area import *

from . import cent
from .cent import *

from . import dist
from .dist import *

from . import prod
from .prod import *

from . import volume
from .volume import *

from . import section
from .section import *

# HELPER FUNCTION
# modVector() - Returns the modulus of a vector
# Return type: float
def modVector(x):
    return ((x[0]*x[0])+(x[1]*x[1])+(x[2]*x[2])) ** 0.5

# dot() - Returns the dot product of the two given vectors.
# Syntax: pyvect.dot(vector_1,vector_2)
# vector_1 - First vector , vector_2 - Second vector
# Return type: int
def dot(x,y):
    dot_product = 0
    for i in range(3):
        dot_product += x[i] * y[i]
    return dot_product

# cross() - Returns the cross product (or) vector of the two given vectors.
# Syntax: pyvect.cross(vector_1,vector_2)
# vector_1 - First vector , vector_2 - Second vector
# Return type: array
def cross(x,y):
    cross_product = []
    cross_product.append(x[1]*y[2] - x[2]*y[1])
    cross_product.append(-(x[0]*y[2] - x[2]*y[0]))
    cross_product.append(x[0]*y[1] - x[1]*y[0])
    return cross_product

# angle() - Returns the angle formed by the two vectors in degrees.
# Syntax: pyvect.angle(vector_1,vector_2)
# vector_1 - First vector , vector_2 - Second vector
# Return type: float
def angle(x,y):
    return np.arccos(dot(x,y)/(modVector(x)*modVector(y)))

# projection() - Returns the projection formed by first vector to the second vector.
# Syntax: pyvect.projection(vector_1,vector_2)
# vector_1 - First vector , vector_2 - Second vector
# Return type: float
def projection(x,y):
    return dot(x,y)/modVector(y)

# isperpendicular() - Returns True if two vectors are perpendicular to each other. (i.e) Dot product of the two vectors is zero.
# Syntax: pyvect.isperpendicular(vector_1,vector_2)
# vector_1 - First vector , vector_2 - Second vector
# Return type: bool
def isperpendicular(x,y):
    return dot(x,y) == 0

# iscollinear() - Returns True if two vectors are collinear. (i.e) Cross product of the two vectors is zero.
# Syntax: pyvect.iscollinear(vector_1,vector_2)
# vector_1 - First vector , vector_2 - Second vector
# Return type: bool
def iscollinear (x,y):
    return any(cross(x,y)) == 0

# unit_vector() - Returns the unit vector of the given vector.
# Syntax: pyvect.unit_vector(vector_1)
# vector_1 - Vector provided to the function
# Return type: array
def unit_vector(x):
    mod = modVector(x)
    x = [i/mod for i in x]
    return x

# unit_normal() - Returns the unit normal vector of given two vectors.
# Syntax: pyvect.unit_normal(vector1,vector2)
# vector_1 - First vector , vector_2 - Second vector
# Return type: array
# Note: First value of the returned list - Postive value of the unit normal
#      Second value of the returned list - Negative value of the unit normal
#      since the sign of unit normal is plus or minus.
def unit_normal(x,y):
    res = unit_vector(cross(x, y))
    return [res, [-i for i in res]]

# bisector() - Returns a vector in the direction of the bisector of the angle between two vectors.
# Syntax: pyvect.bisector(vector_1,vector_2)
# vector_1 - First vector , vector_2 - Second vector
# Return type: array
def bisector(a,b):
    a = unit_vector(a)
    b = unit_vector(b)
    print(a)
    print(b)
    return [a[i] + b[i] for i in range(3)]

# pos_vector() - Returns a position vector between any two given vectors.
# Syntax: pyvect.pos_vector(vector_1,vector_2)
# vector_1 - First vector , vector_2 - Second vector.
# Return type: array
def pos_vector(a,b):
    res = [int(0.5 * (a[i]+b[i])) for i in range(3)]
    return res

# iscoplanar() - Returns the boolean value (True) if the given three vectors satisfy the condition.
# Syntax: pyvect.iscoplanar(vector_1,vector_2,vector_3)
# vector_1 - First vector , vector_2 - Second vector , vector_3 - Third vector.
# Return type: bool
def iscoplanar(a,b,c):
    return dot(cross(a,b),c)==0

# reciprocal() - Returns three reciprocal vector for the given three vectors.
# Syntax: pyvect.reciprocal(vector_1,vector_2,vector_3)
# vector_1 - First vector , vector_2 - Second vector , vector_3 - Third vector.
# Return type: array
def reciprocal(a,b,c):
    res = []
    temp = dot(cross(a,b), c)
    res.append([i/temp for i in cross(b, c)])
    res.append([i/temp for i in cross(c, a)])
    res.append([i/temp for i in cross(a, b)])
    return res

# max_value() - Returns the maximum value between any two given vectors.
# Syntax: pyvect.max_value(vector_1,vector_2)
# vector_1 - First vector , vector_2 - Second vector.
# Return type: int
def max_value(a,b):
    return modVector(a) * modVector(b)

# min_value() - Returns the minimum value between any two given vectors.
# Syntax: pyvect.min_value(vector_1,vector_2)
# vector_1 - First vector , vector_2 - Second vector.
# Return type: int
def min_value(a,b):
    return -(modVector(a) * modVector(b))


import numpy as np
import matplotlib.pyplot as plt

def Taylor_cosseno(n):
    def fat(int):
        p = 1
        for j in range(1, int + 1):
            p *= j
        return p
    x = np.linspace(-8, 8, 1000)

    fig = plt.figure()
    for m in range(1, n + 1):
        cosx = []
        for item in x:
            Soma = 0
            for i in range(m):
                Soma += ((-1) ** i) * (item ** (2 * i)) / fat(2 * i)
            cosx.append(Soma)
        plt.plot(x, cosx)

    plt.axis([-8, 8, -1, 1])
    return plt.show()

Taylor_cosseno(20)


import scipy.integrate as spi
import numpy as np
import pylab as plt

b = 1
gamma = 1/5
TS = 0.1
ND = 70
S0 = 0.99
I0 = 0.01

def diff_eqs(INP, t):
	Y = np.zeros((3))
	V = INP
	Y[0] = - beta * V[0] * V[1]
	Y[1] = beta * V[0] * V[1] - gamma * V[1]
	Y[2] = gamma * V[1]
	return Y

pbdata = np.array([0, 0.2, 0.4, 0.6, 0.8])
psdata = np.linspace(0, 1, num=60)
rdata = [[], [], [], [], []]
for pb in pbdata:
    for ps in psdata:
        INPUT = (S0-ps, I0, 0.0)
        beta = b*(1-pb)
        t_start = 0.0
        t_end = ND
        t_inc = TS
        t_range = np.arange(t_start, t_end+t_inc, t_inc)
        rdata[list(pbdata).index(pb)].append(
            spi.odeint(diff_eqs, INPUT, t_range)[:, 2][-1])

plt.figure(dpi=250, figsize=(12, 10))
plt.title('Global interaction against the density of Prevention')
plt.plot(psdata, rdata[0], lw=2, label='Pb=0.0')
plt.plot(psdata, rdata[1], lw=2, label='Pb=0.2')
plt.plot(psdata, rdata[2], lw=2, label='Pb=0.4')
plt.plot(psdata, rdata[3], lw=2, label='Pb=0.6')
plt.plot(psdata, rdata[4], lw=2, label='Pb=0.8')
plt.legend(loc=0)
plt.xlabel('density of Prevention (Ps)')
plt.ylabel('density of Recovereds (R∞)')
plt.grid(color='navy', linestyle='--', linewidth=0.5)
plt.savefig('R(inf)-Ps Global')
plt.show()

psdata = np.array([0, 0.2, 0.4, 0.6, 0.8])
pbdata = np.linspace(0, 1, num=60)
rdata = [[], [], [], [], []]
for ps in psdata:
    for pb in pbdata:
        INPUT = (S0-ps, I0, 0.0)
        beta = b*(1-pb)
        t_start = 0.0
        t_end = ND
        t_inc = TS
        t_range = np.arange(t_start, t_end+t_inc, t_inc)
        rdata[list(psdata).index(ps)].append(
            spi.odeint(diff_eqs, INPUT, t_range)[:, 2][-1])

plt.figure(dpi=250, figsize=(12, 10))
plt.title('Global interaction against the density of Quarantine')
plt.plot(pbdata, rdata[0], lw=2, label='Ps=0.0')
plt.plot(pbdata, rdata[1], lw=2, label='Ps=0.2')
plt.plot(pbdata, rdata[2], lw=2, label='Ps=0.4')
plt.plot(pbdata, rdata[3], lw=2, label='Ps=0.6')
plt.plot(pbdata, rdata[4], lw=2, label='Ps=0.8')
plt.legend(loc=0)
plt.xlabel('density of Quarantine (Pb)')
plt.ylabel('density of Recovereds (R∞)')
plt.grid(color='navy', linestyle='--', linewidth=0.5)
plt.savefig('R(inf)-Pb Global')
plt.show()

pbdata = np.linspace(0, 1, num=100)
psdata = np.linspace(0, 1, num=100)
rdata = np.zeros((100, 100))
m = 0
for pb in pbdata:
    n = 0
    for ps in psdata:
        INPUT = (S0-ps, I0, 0.0)
        beta = b*(1-pb)
        t_start = 0.0
        t_end = ND
        t_inc = TS
        t_range = np.arange(t_start, t_end+t_inc, t_inc)
        rdata[m][n] = (spi.odeint(diff_eqs, INPUT, t_range)[:, 2][-1])
        n += 1
    m += 1

plt.figure(dpi=250,figsize=(12,10))
c=plt.pcolormesh(psdata, pbdata, rdata, cmap="plasma")
plt.colorbar(c)
plt.title('density of Quarantine - density of Prevention (Global)')
plt.xlabel('density of Prevention (Ps)')
plt.ylabel('density of Quarantine (Pb)')
plt.savefig('Ps-Pb')

Mean Field Theory/SIR-MFT- CQPM-Global.py


import sys
import numpy as np

pos = np.array([int(x)
               for x in sys.stdin.read().strip().split(',')])

part1 = np.sum(np.abs(pos - np.median(pos)), dtype=np.int_)

print('Part 1:', part1)

max_align = np.max(pos)
best_align = (None, 0)
for align in range(max_align + 1):
    distances = np.abs(pos - align)
    cost = np.sum((distances * (distances + 1)) / 2, dtype=np.int_)
    if best_align[0] is None or cost < best_align[1]:
        best_align = (align, cost)

print('Part 2', best_align[1])


import numpy as np

class LinearRegressor:
    def __init__(self, num_features=2, iterations=100, alpha=0.0001, of=None):
        self.out = of
        self.num_features = num_features + 1  # remember to include the intercept...
        self.alpha = alpha
        self.iterations = iterations
        self.weights = np.zeros(self.num_features)

    def predict(self, data):  # given alpha row, what is the predicted value
        return np.dot(data, self.weights)

    def fit(self, data, expectations):
        e = 0.000001  # minimum change needed to declare convergence
        for i in range(self.iterations):
            v = self.weights.copy()
            self.fit_incremental_gradient_descent(data, expectations)
            total_adj = sum([abs(wi - vi) for wi, vi in zip(self.weights, v)])
            if total_adj < e:
                break
        self.out.write("%0.4f, %d, %0.4f, %0.4f, %0.4f\n" % (
        self.alpha, self.iterations, self.weights[0], self.weights[1], self.weights[2]))

    def fit_incremental_gradient_descent(self, X: np.ndarray, Y: np.ndarray):
        w = self.weights;
        h = self.predict  # abbreviations
        n = w.shape[0]
        for i, x_i in enumerate(X):  # for each x_i in X (the training data set)
            for j in range(n):  # for each feature in the test data instance, x_i,j
                w[j] -= self.adj_weight(Y[i], h(x_i), x_i[j], self.alpha)

    @staticmethod
    def adj_weight(y, hx, xij, a):
        # get a proportional fraction of the feature and remove from the corresponding weight
        try:
            return a * xij * (hx - y)
        except OverflowError:
            return 0.0


import typing

import numba as nb
import numpy as np

@nb.njit((nb.i8, nb.i8), cache=True)
def ext_gcd(a: int, b: int) -> typing.Tuple[int, int, int]:
  if not b: return a, 1, 0
  g, s, t = ext_gcd(b, a % b)
  return g, t, s - a // b * t

@nb.njit((nb.i8, nb.i8), cache=True)
def mod_inv(a: int, mod: int) -> int:
  g, p, q = ext_gcd(a, mod)
  if g != 1:
    raise ArithmeticError(
      'modular multiplicative inverse does not exist.'
    )
  return p % mod

# @nb.njit((nb.i8[:], nb.i8[:], nb.i8), cache=True)
# def garner(
#   r: np.ndarray,
#   m: np.ndarray,
#   mod: int,
# ) -> int:
#   n = r.size
#   assert m.size == n
#   m = np.hstack((m, np.array([mod])))
#   s = np.zeros(n + 1, np.int64)
#   m_prod = np.full(n + 1, 1, np.int64)
#   for i in range(n):
#     t = (r[i] - s[i]) % m[i] * mod_inv(m_prod[i], m[i]) % m[i]
#     s[i + 1:] += t * m_prod[i + 1:] % m[i + 1:]
#     s %= m
#     m_prod = m_prod * m[i] % m
#   return s[-1], m_prod[-1]

@nb.njit((nb.i8[:], nb.i8[:]), cache=True)
def garner(
  r: np.ndarray,
  m: np.ndarray,
) -> int:
  n = r.size
  assert m.size == n
  x = 0
  m_prod = 1
  for i in range(n):
    t = (r[i] - x) * mod_inv(m_prod, m[i]) % m[i]
    x += t * m_prod
    m_prod *= m[i]
  return x

@nb.njit((nb.i8, ) * 3, cache=True)
def solve(n: int, s: int, k: int) -> typing.NoReturn:
  d, _, _ = ext_gcd(n, k)
  if s % d:
    print(-1)
    return
  n //= d
  k //= d
  s //= d
  r = np.array([0, s])
  m = np.array([n, k])
  y = garner(r, m)
  lcm = n * k * d
  if y < s: y += lcm
  print((y - s) // k)

def main() -> typing.NoReturn:
  t = int(input())
  for _ in range(t):
    n, s, k = map(int, input().split())
    solve(n, s, k)

main()


from math import floor
import numpy as np

def row_col_to_seq(row_col, num_cols):
    return row_col[:,0] * num_cols + row_col[:,1]

def seq_to_col_row(seq, num_cols):
    r = floor(seq / num_cols)
    c = seq - r * num_cols
    return np.array([[r, c]])

def create_policy_direction_arrays(model, policy):
    """
     define the policy directions
     0 - up    [0, 1]
     1 - down  [0, -1]
     2 - left  [-1, 0]
     3 - right [1, 0]
    :param policy:
    :return:
    """
    # action options
    UP = 0
    DOWN = 1
    LEFT = 2
    RIGHT = 3

    # intitialize direction arrays
    U = np.zeros((model.num_rows, model.num_cols))
    V = np.zeros((model.num_rows, model.num_cols))

    for state in range(model.num_states-1):
        # get index of the state
        i = tuple(seq_to_col_row(state, model.num_cols)[0])
        # define the arrow direction
        if policy[state] == UP:
            U[i] = 0
            V[i] = 0.5
        elif policy[state] == DOWN:
            U[i] = 0
            V[i] = -0.5
        elif policy[state] == LEFT:
            U[i] = -0.5
            V[i] = 0
        elif policy[state] == RIGHT:
            U[i] = 0.5
            V[i] = 0

    return U, V


import numpy as np
from numba import njit, prange, config

# config.THREADING_LAYER = 'tbb'
# to use this, first do: conda install tbb

def lu_0(A):
    assert A.shape[0] == A.shape[1]

    U = A.astype(np.float64)
    n = A.shape[0]
    L = np.eye(n).astype(np.float64)

    for k in range(n - 1):
        u_k_k = U[k, k]
        u_k_kn = U[k, k:n]
        for j in range(k + 1, n):
            L[j, k] = U[j, k] / u_k_k
            U[j, k:n] -= L[j, k] * u_k_kn

    return L, U

def lu_1(A):
    assert A.shape[0] == A.shape[1]

    A = A.astype(np.float64)
    n = A.shape[0]

    for k in range(n - 1):
        for i in range(k + 1, n):
            lambda_ = A[i, k] / A[k, k]
            A[i, k + 1: n] -= lambda_ * A[k, k + 1: n]
            A[i, k] = lambda_

    lower = np.tril(A, k=-1)
    np.fill_diagonal(lower, 1)

    upper = np.triu(A, k=0)

    return lower, upper

def lu_2(A):
    assert A.shape[0] == A.shape[1]

    A = A.astype(np.float64)
    n = A.shape[0]

    for k in range(n - 1):
        a_k_k = A[k, k]
        a_k_k1_n = A[k, k + 1: n]

        for i in range(k + 1, n):
            lambda_ = A[i, k] / a_k_k
            A[i, k + 1: n] -= lambda_ * a_k_k1_n
            A[i, k] = lambda_

    lower = np.tril(A, k=-1)
    np.fill_diagonal(lower, 1)

    upper = np.triu(A, k=0)

    return lower, upper

def lu_3(A):
    assert A.shape[0] == A.shape[1]

    n = A.shape[0]
    U = A.astype(np.float64)
    L = np.eye(n)
    for i in range(1, n):
        for j in range(i):
            L[i, j] = U[i, j] / U[j, j]
            for k in range(j, n):
                U[i, k] -= L[i, j] * U[j, k]
    return L, U

def lu_4(A):
    assert A.shape[0] == A.shape[1]

    n = A.shape[0]
    U = A.astype(np.float64)
    L = np.eye(n)
    for i in range(1, n):
        for j in range(i):
            L[i, j] = U[i, j] / U[j, j]
            U[i, :] -= L[i, j] * U[j, :]
    return L, U

def lu_5(A):
    assert A.shape[0] == A.shape[1]

    A = A.astype(np.float64)
    n = A.shape[0]

    for k in range(n - 1):
        A[k + 1: n, k] /= A[k, k]
        A[k + 1: n, k + 1: n] -= np.outer(A[k + 1: n, k], A[k, k + 1: n])

    lower = np.tril(A, k=-1)
    np.fill_diagonal(lower, 1)

    upper = np.triu(A, k=0)

    return lower, upper

@njit(parallel=True)
def lu_parallel(x):
    upper = np.asfortranarray(np.zeros(x.shape, dtype=np.float64))
    n = len(x)
    lower = np.eye(n)

    for i in range(n):

        for k in prange(i, n):
            total = 0.0
            for j in range(i):
                total += lower[i][j] * upper[j][k]

            upper[i][k] = x[i][k] - total

        for k in prange(i + 1, n):
            total = 0.0
            for j in range(i):
                total += lower[k][j] * upper[j][i]

            lower[k][i] = (x[k][i] - total) / upper[i][i]

    return lower, upper

@njit(parallel=True)
def lu_parallel_2(x):
    upper = np.asfortranarray(np.zeros(x.shape, dtype=np.float64))
    n = len(x)
    lower = np.ascontiguousarray(np.eye(n))

    for i in range(n):

        for k in prange(i, n):
            total = lower[i, :] @ upper[:, k]
            upper[i][k] = x[i][k] - total

        for k in prange(i + 1, n):
            total = lower[k, :] @ upper[:, i]
            lower[k][i] = (x[k][i] - total) / upper[i][i]

    return lower, upper


import numpy as np
import pdb
import copy

class DualNumber(object):
    """ Class which implements dual numbers

        x = r + e d with e**2 = 0

        x1 + x2 = r1 + r2 + e ( d1 + d2)
        x1 - x2 = r1 - r2 + e ( d1 - d2)
        x1 * x2 = (r1 * r2) + e ( r1 * d2 + d1 * r2 )
        conj(x) = r - e d
        1/x = 1/r  - e d/r**2

    """
    def __init__(self,r,d):
        """
        r : real part
        e : dual part

        """
        self.r = r
        self.d = d
        self.shape = np.shape(self.r)

    def __repr__(self):
        st=''
        st+=str(self.r)+' + '
        st+=str(self.d)+' e'
        return(st)

    def __neg__(self):
        return DualNumber(-self.r,-self.d)

    def __add__(self,other):
        return DualNumber(self.r+other.r,self.d+other.d)

    def __sub__(self,other):
        return DualNumber(self.r-other.r,self.d-other.d)

    def __mul__(self,other):
        c=self.r*other.r
        d=self.r*other.d+self.d*other.r
        return DualNumber(c,d)

    def __div__(self,other):
        #return(DualNumber(1.0*self.n/other.n,(self.d*other.n-self.n*other.d)/(1.0*other.n**2)))
        return(self*other.inv())

    def __getitem__(self,key):
        return DualNumber(self.r[key],self.d[key])

    def __setitem__(self,key,val):
        self.r[key] = val.r[key]
        self.d[key] = val.d[key]

    def sqrt(self):
        return(DualNumber(np.sqrt(self.r),self.d/(2*np.sqrt(self.r))))

    def inv(self):
        assert(self.r!=0), "pure dual numbers have no inverse"
        return(DualNumber(1./self.r,-1.0*self.d/(self.r**2)))

class Quaternion(object):

    def __init__(self,a=1,b=0):
        """
        a : np.array (Nx4) dtype=complex
        b : np.array (Nx4) dtype=complex
        """
        if type(a)!=np.ndarray:
            self.a=np.array([a])[:,None].astype(complex)
            self.b=np.array([b])[:,None].astype(complex)
        else:
            if len(a.shape)==2:
                self.a=a.astype(complex)
                self.b=b.astype(complex)
            else:
                self.a = a[:,None].astype(complex)
                self.b = b[:,None].astype(complex)

        assert(self.a.shape==self.b.shape)
        self.shape = np.shape(self.a)

    def __repr__(self):
        st = ''
        st += str(self.a.real)+' + '
        st += str(self.a.imag)+'i + '
        st += str(self.b.real)+'j + '
        st += str(self.b.imag)+'k'
        return st

    def __neg__(self):
        return Quaternion(-self.a,-self.b)

    def __add__(self,other):
        return Quaternion(self.a+other.a,self.b+other.b)

    def __sub__(self,other):
        return Quaternion(self.a-other.a,self.b-other.b)

    def __mul__(self,other):
        c = self.a*other.a-self.b*other.b.conjugate()
        d = self.a*other.b+self.b*other.a.conjugate()
        return Quaternion(c,d)

    def __rmul__(self,k):
        return Quaternion(self.a*k,self.b*k)

    def __abs__(self):
        return np.hypot(abs(self.a),abs(self.b))

    def __getitem__(self,key):
        return Quaternion(self.a[key],self.b[key])

    def __setitem__(self,key,val):
        self.a[key] = copy.copy(val.a)
        self.b[key] = copy.copy(val.b)

    def normalize(self):
        return (1/abs(self))*self

    def conjugate(self):
        return Quaternion(self.a.conjugate(),-self.b)

    def polar(self):
        mq = abs(self)
        qu = (1./mq)*self
        return(mq,qu)

    def scal(self):
        return self.a.real

    def vect(self):
        v = np.hstack((self.a.imag,self.b.real,self.b.imag)).T
        return(v)

    def vecang(self):
        v = self.vect()
        cas2 = self.scal()
        sas2 = np.sqrt(np.sum(v*v))
        vn = v/sas2
        angle = 2.*np.arctan2(sas2,cas2)
        return(vn,angle)

    def from_mat(self,M):
        tr = np.trace(M)
        if (tr > 0):
            S = np.sqrt(tr+1.0)*2
            self.a = 0.25 * S +1j* (M[2,1] - M[1,2])/S
            self.b = (M[0,2]-M[2,0]/S)+1j*(M[1,0]-M[0,1])/S
        elif ((M[0,0] > M[1,1])&(M[0,0]>M[2,2])):
            S = np.sqrt(1.0 + M[0,0] - M[1,1] - M[2,2])*2
            self.a = (M[2,1] - M[1,2])/S +1j*0.25*S
            self.b = (M[0,1] + M[1,0])/S + 1j*(M[0,2] + M[2,0])/S
        elif (M[1,1] > M[2,2]):
            S = np.sqrt(1.0 + M[1,1] - M[0,0]  - M[2,2])*2
            self.a = (M[0,2]  - M[2,0])/S+1j*(M[0,1]+M[1,0])/S
            self.b = 0.25*S+1j* (M[1,2]  + M[2,1]) / S
        else:
            S = np.sqrt(1.0 + M[2,2]  - M[0,0]  - M[1,1])*2
            self.a = (M[1,0] - M[0,1])/S +1j* (M[0,2]  + M[2,0])/S
            self.b = (M[1,2] + M[2,1])/S +1j* 0.25 * S

    def __div__(self,other):
        u  = 1./abs(other)**2
        qu = Quaternion(u+0j,np.zeros(u.shape)+0j)
        qv  = qu*other.conjugate()
        return self*qv

    def log(self):
        q = abs(self)
        v = self.vect()
        if len(v.shape)>1:
            vn = v/np.sqrt(np.sum(v*v,axis=1))
            v2 = np.arccos(self.a.real/q)*vn
            a = np.log(q) + 1j*v2[:,0]
            b = v2[:,1]+1j*v2[:,2]
        else:
            vn = v/np.sqrt(np.sum(v*v))
            v2 = np.arccos(self.a.real/q)*vn
            a = np.log(q) + 1j*v2[0]
            b = v2[1]+1j*v2[2]
        return(Quaternion(a,b))

    def exp(self):
        """ exponential of a quaternion
        """

        ea = np.exp(self.a.real)
        v = self.vect()
        if len(v.shape)>1:
            theta = np.sqrt(np.sum(v*v,axis=1))
            vn = v/theta
            a = np.cos(theta)+1j*np.sin(theta)*vn[:,0]
            b = np.sin(theta)*(vn[:,1]+1j*vn[:,2])
        else:
            theta = np.sqrt(np.sum(v*v))
            vn = v/theta
            a = np.cos(theta)+1j*np.sin(theta)*vn[0]
            b = np.sin(theta)*(vn[1]+1j*vn[2])

        ev = Quaternion(a,b)
        return(ea*ev)

    def __pow__(self,n):
        r=1
        for i in range(n):
            r=r*self
        return r

class DualQuaternion(object):
    """

    For more details about Dual quaternions see the following ref

    www.euclideanspace.com/maths/algebra/realNormedAlgebra/other/dualQuaternion
    https://en.wikipedia.org/wiki/Dual_quaternion

    Real Time skeletal Animation (Ladislav Kavan) 2007
    https://www.cs.utah.edu/~ladislav/thesis/LKthesisHiresPrint.pdf

    """

    def __init__(self,qr,qd):
        """
        Parameters
        ----------

        qr : Quaternion
            real part
        qd : Quaternion
            dual part

        """
        #assert(qr.shape==qd.shape), "%r , %r   " % (qr.shape,qd.shape)
        assert  (qr.shape==qd.shape), pdb.set_trace()
        self.qr = qr
        self.qd = qd
        self.shape = self.qr.shape

    def __repr__(self):
        st=''
        st+=str(self.qr.scal())+'+'
        if len(self.qr.vect().shape)>1:
            st +=str(self.qr.vect()[:,0])+' i +'
            st +=str(self.qr.vect()[:,1])+' j +'
            st +=str(self.qr.vect()[:,2])+' k +'
            st +=str(self.qd.scal())+' e +'
            st +=str(self.qd.vect()[:,0])+' ei+'
            st +=str(self.qd.vect()[:,1])+' ej+'
            st +=str(self.qd.vect()[:,2])+' ek'
        else:
            st +=str(self.qr.vect()[0])+' i +'
            st +=str(self.qr.vect()[1])+' j +'
            st +=str(self.qr.vect()[2])+' k +'
            st +=str(self.qd.scal())+' e +'
            st +=str(self.qd.vect()[0])+' ei+'
            st +=str(self.qd.vect()[1])+' ej+'
            st +=str(self.qd.vect()[2])+' ek'

        return (st)

    def __neg__(self):
        return DualQuaternion(-self.qr,-self.qd)

    def __add__(self,other):
        return DualQuaternion(self.qr+other.qr,self.qd+other.qd)

    def __sub__(self,other):
        return DualQuaternion(self.qr-other.qr,self.qd-other.qd)

    def __rmul__(self,s):
        return DualQuaternion(s*self.qr,s*self.qd)

    def __mul__(self,other):
        c = self.qr*other.qr
        d = self.qr*other.qd + self.qd*other.qr
        return DualQuaternion(c,d)

    def __getitem__(self,key):
        return DualQuaternion(self.qr[key],self.qd[key])

    def __setitem__(self,key,val):
        self.qr[key] = val.qr
        self.qd[key] = val.qd

    def conj1(self):
        """
            Quaternion conjugaison

            (Q1 Q2)* = Q2*Q1*

        """
        return DualQuaternion(self.qr.conjugate(),self.qd.conjugate())

    def conj2(self):
        """
            Dual conjugaison
        """
        return DualQuaternion(self.qr,-self.qd)

    def conj3(self):
        """
            Quaternion Dual conjugaison
            ( conjugaison used for chaining displacement )

            dis * dqp * dis.conj3

        """
        return DualQuaternion(self.qr.conjugate(),-self.qd.conjugate())

    def abs(self):
        dq = self*self.conjugate()
        assert(np.allclose(dq.qr.vect(),0))
        assert(np.allclose(dq.qd.vect(),0))
        magnitude2 = DualNumber(dq.qr.scal(),dq.qd.scal())
        magnitude = magnitude2.sqrt()
        return(magnitude)

class QPoint(Quaternion):
    """ Quaternion point

    The scalar part is 0

    """
    def __init__(self,pt):
        if len(pt.shape)==2:
            Quaternion.__init__(self,1j*pt[:,0][:,None],pt[:,1][:,None]+1j*pt[:,2][:,None])
        else:
            Quaternion.__init__(self,1j*np.array([pt[0]])[:,None]
                                       ,np.array([pt[1]])[:,None]
                                    +1j*np.array([pt[2]])[:,None])

class DQPoint(DualQuaternion):
    """ Dual quaternion point

    The real part is unitary purely scalar quaternion
    The dual part is a QPoint

    """
    def __init__(self,pt):
        if len(pt.shape)>1:
            N = pt.shape[0]
        else:
            N = 1
        DualQuaternion.__init__(self,Quaternion(np.ones((N,1)),np.zeros((N,1))),QPoint(pt))

class Displacement(DualQuaternion):
    """ Class implementing a displacement

    A displacement is a DualQuaternion built from a rotation and a translation

    """

    def __init__(self,qr=Quaternion(1+0j,0+0j),qt=Quaternion(0j,0+0j)):
        """

        qr : unitary quaternion
        qt : pure vector

        """
        assert(np.allclose(abs(qr),1))
        assert(np.allclose(qt.scal(),0))
        DualQuaternion.__init__(self,qr,0.5*qr*qt)

    def setdis(self,v):
        self.qd = 0.5*QPoint(v)

    def setrotation(self,qr):
        self.qr = qr


import numpy as np
import torch
import torch.nn as nn

"""
The modules/function here implement custom versions of batch normalization in PyTorch.
In contrast to more advanced implementations no use of a running mean/variance is made.
You should fill in code into indicated sections.
"""

######################################################################################
# Code for Question 3.1
######################################################################################

class CustomBatchNormAutograd(nn.Module):
    """
    This nn.module implements a custom version of the batch norm operation for MLPs.
    The operations called in self.forward track the history if the input tensors have the
    flag requires_grad set to True. The backward pass does not need to be implemented, it
    is dealt with by the automatic differentiation provided by PyTorch.
    """

    def __init__(self, n_neurons, eps=1e-5):
        """
        Initializes CustomBatchNormAutograd object.

        Args:
          n_neurons: int specifying the number of neurons
          eps: small float to be added to the variance for stability

        TODO:
          Save parameters for the number of neurons and eps.
          Initialize parameters gamma and beta via nn.Parameter
        """
        super(CustomBatchNormAutograd, self).__init__()

        ########################
        # PUT YOUR CODE HERE  #
        #######################
        self.n_neurons = n_neurons
        self.eps = eps
        self.gammas = torch.nn.Parameter(torch.ones(n_neurons))
        self.betas = torch.nn.Parameter(torch.zeros(n_neurons))
        ########################
        # END OF YOUR CODE    #
        #######################

    def forward(self, input):
        """
        Compute the batch normalization

        Args:
          input: input tensor of shape (n_batch, n_neurons)
        Returns:
          out: batch-normalized tensor

        TODO:
          Check for the correctness of the shape of the input tensor.
          Implement batch normalization forward pass as given in the assignment.
          For the case that you make use of torch.var be aware that the flag unbiased=False should be set.
        """

        ########################
        # PUT YOUR CODE HERE  #
        #######################

        shape = input.size()

        if len(shape) == 1:
            input.view(1, -1)
            shape = input.size()
        if len(shape) > 2:
            raise ValueError('Expected 1-D or 2-D tensor (got {})'.format(str(shape)))
        elif input.shape[1] != self.n_neurons:
            raise ValueError('Expected _ x {} tensor (got {} x {})'.format(str(self.n_neurons), str(shape[0]), str(shape[1])))

        x = input
        mu = x.mean(dim=0)
        std = x.std(dim=0)
        x_hat = (x - mu) / torch.sqrt(std * std + self.eps)
        out = self.gammas * x_hat + self.betas

        ########################
        # END OF YOUR CODE    #
        #######################

        return out

######################################################################################
# Code for Question 3.2 b)
######################################################################################

class CustomBatchNormManualFunction(torch.autograd.Function):
    """
    This torch.autograd.Function implements a functional custom version of the batch norm operation for MLPs.
    Using torch.autograd.Function allows you to write a custom backward function.
    The function will be called from the nn.Module CustomBatchNormManualModule
    Inside forward the tensors are (automatically) not recorded for automatic differentiation since the backward
    pass is done via the backward method.
    The forward pass is not called directly but via the apply() method. This makes sure that the context objects
    are dealt with correctly. Example:
      my_bn_fct = CustomBatchNormManualFunction()
      normalized = fct.apply(input, gamma, beta, eps)
    """

    @staticmethod
    def forward(ctx, input, gamma, beta, eps=1e-5):
        """
        Compute the batch normalization

        Args:
          ctx: context object handling storing and retrival of tensors and constants and specifying
               whether tensors need gradients in backward pass
          input: input tensor of shape (n_batch, n_neurons)
          gamma: variance scaling tensor, applied per neuron, shpae (n_neurons)
          beta: mean bias tensor, applied per neuron, shpae (n_neurons)
          eps: small float added to the variance for stability
        Returns:
          out: batch-normalized tensor

        TODO:
          Implement the forward pass of batch normalization
          Store constant non-tensor objects via ctx.constant=myconstant
          Store tensors which you need in the backward pass via ctx.save_for_backward(tensor1, tensor2, ...)
          Intermediate results can be decided to be either recomputed in the backward pass or to be stored
          for the backward pass. Do not store tensors which are unnecessary for the backward pass to save memory!
          For the case that you make use of torch.var be aware that the flag unbiased=False should be set.
        """

        ########################
        # PUT YOUR CODE HERE  #
        #######################
        x = input
        mu = x.mean(dim=0)
        std = x.std(dim=0, unbiased=False)
        var = torch.sqrt(std * std + eps)
        x_centered = x - mu
        x_hat = x_centered / var
        out = gamma * x_hat + beta

        ctx.save_for_backward(x, mu, std, var, gamma)
        ########################
        # END OF YOUR CODE    #
        #######################

        return out

    @staticmethod
    def backward(ctx, grad_output):
        """
        Compute backward pass of the batch normalization.

        Args:
          ctx: context object handling storing and retrival of tensors and constants and specifying
               whether tensors need gradients in backward pass
        Returns:
          out: tuple containing gradients for all input arguments

        TODO:
          Retrieve saved tensors and constants via ctx.saved_tensors and ctx.constant
          Compute gradients for inputs where ctx.needs_input_grad[idx] is True. Set gradients for other
          inputs to None. This should be decided dynamically.
        """
        ########################
        # PUT YOUR CODE HERE  #
        #######################
        # import pydevd
        # pydevd.settrace(suspend=False, trace_only_current_thread=True)

        (x, mu, std, var, gamma) = ctx.saved_tensors
        grad_input, grad_gamma, grad_beta = None, None, None

        x_hat = (x - mu) / var

        if ctx.needs_input_grad[2]:
            grad_beta = grad_output.sum(0)

        if ctx.needs_input_grad[1]:
            grad_gamma = (grad_output * x_hat).sum(0)

        if ctx.needs_input_grad[0]:
            c1 = grad_output.mean(dim=0)
            c2 = (grad_output * x_hat).mean(dim=0)
            grad_input = (gamma / var) * (grad_output - c1 - x_hat * c2)

        ########################
        # END OF YOUR CODE    #
        #######################

        # return gradients of the three tensor inputs and None for the constant eps
        return grad_input, grad_gamma, grad_beta, None

######################################################################################
# Code for Question 3.2 c)
######################################################################################

class CustomBatchNormManualModule(nn.Module):
    """
    This nn.module implements a custom version of the batch norm operation for MLPs.
    In self.forward the functional version CustomBatchNormManualFunction.forward is called.
    The automatic differentiation of PyTorch calls the backward method of this function in the backward pass.
    """

    def __init__(self, n_neurons, eps=1e-5):
        """
        Initializes CustomBatchNormManualModule object.

        Args:
          n_neurons: int specifying the number of neurons
          eps: small float to be added to the variance for stability

        TODO:
          Save parameters for the number of neurons and eps.
          Initialize parameters gamma and beta via nn.Parameter
        """
        super(CustomBatchNormManualModule, self).__init__()

        ########################
        # PUT YOUR CODE HERE  #
        #######################
        self.n_neurons = n_neurons
        self.eps = eps
        self.gammas = torch.nn.Parameter(torch.ones(n_neurons))
        self.betas = torch.nn.Parameter(torch.zeros(n_neurons))
        ########################
        # END OF YOUR CODE    #
        #######################

    def forward(self, input):
        """
        Compute the batch normalization via CustomBatchNormManualFunction

        Args:
          input: input tensor of shape (n_batch, n_neurons)
        Returns:
          out: batch-normalized tensor

        TODO:
          Check for the correctness of the shape of the input tensor.
          Instantiate a CustomBatchNormManualFunction.
          Call it via its .apply() method.
        """

        ########################
        # PUT YOUR CODE HERE  #
        #######################
        shape = input.size()

        if len(shape) == 1:
            input.view(1, -1)
            shape = input.size()
        if len(shape) > 2:
            raise ValueError('Expected 1-D or 2-D tensor (got {})'.format(str(shape)))
        elif input.shape[1] != self.n_neurons:
            raise ValueError(
                'Expected _ x {} tensor (got {} x {})'.format(str(self.n_neurons), str(shape[0]), str(shape[1])))

        fct = CustomBatchNormManualFunction()
        out = fct.apply(input, self.gammas, self.betas, self.eps)
        ########################
        # END OF YOUR CODE    #
        #######################

        return out


"""LogNormal distribution
"""
import numpy as np
from scipy.stats import lognorm

from xgboost_distribution.distributions.base import BaseDistribution
from xgboost_distribution.distributions.utils import check_is_gt_zero

class LogNormal(BaseDistribution):
    """LogNormal distribution with log scoring.

    Definition:

        f(x) = exp( -[ (log(x) - log(scale)) / (2 s^2) ]^2 / 2 ) / s

    with parameters (scale, s).

    We reparameterize:
        s -> log(s) = a
        scale -> log(scale) = b

    Note that b essentially becomes the 'loc' of the distribution:

        log(x/scale) / s = ( log(x) - log(scale) ) / s

    which can then be taken analogous to the normal distribution's

        (x - loc) / scale

    Hence we can re-use the computations in `distribution.normal`, exchanging:

        y -> log(y)
        scale -> s

    """

    @property
    def params(self):
        return ("scale", "s")

    def check_target(self, y):
        check_is_gt_zero(y)

    def gradient_and_hessian(self, y, params, natural_gradient=True):
        """Gradient and diagonal hessian"""

        log_y = np.log(y)

        loc, log_s = self._split_params(params)  # note loc = log(scale)
        var = np.exp(2 * log_s)

        grad = np.zeros(shape=(len(y), 2))
        grad[:, 0] = (loc - log_y) / var
        grad[:, 1] = 1 - ((loc - log_y) ** 2) / var

        if natural_gradient:
            fisher_matrix = np.zeros(shape=(len(y), 2, 2))
            fisher_matrix[:, 0, 0] = 1 / var
            fisher_matrix[:, 1, 1] = 2

            grad = np.linalg.solve(fisher_matrix, grad)

            hess = np.ones(shape=(len(y), 2))  # we set the hessian constant
        else:
            hess = np.zeros(shape=(len(y), 2))  # diagonal elements only
            hess[:, 0] = 1 / var
            hess[:, 1] = 2 * ((log_y - loc) ** 2) / var

        return grad, hess

    def loss(self, y, params):
        scale, s = self.predict(params)
        return "LogNormalError", -lognorm.logpdf(y, s=s, scale=scale).mean()

    def predict(self, params):
        log_scale, log_s = self._split_params(params)
        scale, s = np.exp(log_scale), np.exp(log_s)

        return self.Predictions(scale=scale, s=s)

    def starting_params(self, y):
        log_y = np.log(y)
        return np.mean(log_y), np.log(np.std(log_y))

    def _split_params(self, params):
        """Return log_scale (loc) and log_s from params"""
        return params[:, 0], params[:, 1]