# -*- coding: utf-8 -*-
"""
Contains all the formulations one want to know
"""

# I MAKE SURE YOUR ENVIRONMENT IS READY ######################################
'''
### I.1 check that you have all the libraries

to get all the libraries you are going to need
`!pip freeze > requirement.txt -v`

### I.2 Tell python where pygemmes is
pygemmes has to be found by python so that it can loads it

there are two methods :
    a) Start your python terminal at the root of the library ( typically ...\\GitHub\\GEMMES )
or indicate it to spyder if you're using it (top-right folder)
    b) Indicate to the system path where it is. In my case I'd have to use the lines :

```
import sys  # a library that help python know where things are
path = "C:\\Users\\Paul Valcke\\Documents\\GitHub\\GEMMES"  # Where pygemmes is
sys.path.insert(0, path)  # we tell python to look at the folder `path`
```

If you do a) you do it once and you're ready to go !
if you do b) you have to do it every time

########## ONCE IT'S DONE YOU SHOULD RESTART YOUR IPYTHON TERMINAL ###########
'''
import sys
path = "C:\\Users\\Paul Valcke\\Documents\\GitHub\\GEMMES"  # Where pygemmes is
sys.path.insert(0, path)  # we tell python to look at the folder `path`

import matplotlib.pyplot as plt
import cv2
import numpy as np
import pygemmes as pgm  # we rename pygemmes as pgm to be shorter

# %% PYTHON 101
'''
Python is a user-friendly, flexible language, with a huge community (lot of libraries),
and incredibly powerful when used well

Here are a few practical examples
'''

# There are multiple types of variables

A = 'oneword'  # A string (characters)
B = "multiple words"  # A string too !
C = ''' A long description
That can be on multiple lines '''

D = 1
E = 1.1
F = True
G = E > F

H = {}
keyvar = 'key'
H[keyvar] = 42
H['test'] = C
H[C] = 0  # You can put anything as a list !
H[3] = 'plop'
I = {}
I['dictionnary'] = H  # A dictionnary inside a dictionnary

# Elements can be put together
J = (A, D, H)  # A tuple (a list that cannot be modified)
K = [A, D, H]  # A list

# Loops work with indentations
for i in [1, 2, 3, 4]:
    print(i)

# Dictionnaries are objects, and objects have methods associated :
for key, value in H.items():
    print(key, value)

# You can create your own objects

def IAmAFunction(x, hello=1):
    '''
    This is a description to explain what is in the function
    '''

    y = x+1  # y is created locally, once we leave the function we cannot acces y
    if hello:
        y *= 2  # we have a if loop for fun, hello=0, hello=False or hello=None does not go in this section
    return y-7  # the value that you get at the end

print(IAmAFunction(45))  # hello will take the value 1
print(IAmAFunction(34))
print(IAmAFunction(34, hello=False))  # hello is taking the non-default value

# USE ARRAYS !
A = np.linspace(0, 1, 100)
B = np.linspace(100, 200, 100)

C = A*B
D = np.zeros(100)  # create 100 zeros
for i in range(100):
    D[i] = A[i]*B[i]
print(C-D)

# %% OVERVIEWS : WHAT IS IN PYGEMMES ?
pgm.get_dfields_overview()
pgm.get_available_solvers()
pgm.get_available_models(details=True, verb=True)
pgm.get_available_output()

listofsolver = pgm.get_available_solvers(returnas=list)
listofmodels = pgm.get_available_models(returnas=list)
listoffields = [v[0] for v in pgm.get_dfields_overview(returnas=list)]

# %% A FEW SIMPLE FUNCTIONS TO SHOW A FEW POSSIBILITIES
pgm.comparesolver_Lorenz(dt=0.01, Npoints=10000)
pgm.plot_one_run_all_solvers('LorenzSystem', preset='Canonical')
pgm.plot_one_run_all_solvers('GK')
pgm.testConvergence_DampOsc([1, 0.1, 0.01, 0.001], solver='eRK4-homemade')

# %% HUB : YOUR BEST FRIEND
hub = pgm.Hub('GK',)
# verb=False
# preset=None,
# dpresets=None,
# verb=None)

hub

pgm.Generate_network_logics('GK')
hub.equations_description()

hub.dmodel  # Gives the content of the model file
hub.dmisc  # gives multiple informations on the run and the variables

hub.get_summary()

hub.run(verb=0)  # solver=listofsolver[0],verb=1.1)
R = hub.get_dparam(returnas=dict)

hub.get_summary()

# Plots examples
dax = hub.plot()
dax2 = hub.plot(key=['lambda', 'omega', 'd'])  # Select the variables
dax3 = hub.plot(key=('GDP', 'a', 'Pi', 'kappa'))  # Remove some variables
pgm._plots.phasespace(hub, x='omega', y='lambda', color='d', idx=0)

# Fill Cycles
hub.FillCyclesForAll(ref='lambda')
dax4 = hub.plot(mode='cycles')

# %% Practical things about get_dparam
'''
get_dparam(self,
           condition=None,
           verb=None,
           returnas=None,
           **kwdargs):
        """
        Return a copy of the input parameters dict as:
            - dict: dict
            - 'DataGFrame': a pandas DataFrame
            - np.ndarray: a dict of np.ndarrays
            - False: return nothing (useful of verb=True)
        verb:
            - True: pretty-print the chosen parameters
            - False: print nothing
        """
        lcrit = ['key', 'dimension', 'units', 'type', 'group', 'eqtype']
        lprint = ['parameter', 'value', 'units', 'dimension', 'symbol',
            'type', 'eqtype', 'group', 'comment',
        ]
'''
R = hub.get_dparam(returnas=dict)
groupsoffields = hub.get_dparam_as_reverse_dict(crit='units', eqtype=['ode', 'statevar'])
print(groupsoffields)

# %% Example : gaussian system
hub_noise = pgm.Hub('Noise')
hub_noise.run()
dax = hub_noise.plot(key=['y'], label='-1')
for i in range(10):
    hub_noise.run()
    dax = hub_noise.plot(key=['y'], dax=dax, label=i)

# %% CHANGING VALUES
hub = pgm.Hub('GK', verb=False)
# One slowly
hub.set_dparam(key='dt', value=0.01)
hub.set_dparam(Tmax=50)

# Send a dictionnary
dparam = {'alpha': 0, 'beta': 1}
hub.set_dparam(dparam=dparam)

# Send a dictionnary (alternative)
dparam_changes = {'alpha': 0., 'delta': 0.}
hub.set_dparam(dparam_changes)

# Create N system in parrallel with different values
hub.set_dparam(alpha=[0, 0.01, 0.02, 0.03])

# Load a preset
hub = pgm.Hub('GK', verb=False)
hub.set_dparam(preset='default')

# %% COMPARING MODELS REDUCED AND NON-REDUCED
pgm.get_available_models(details=False)
pgm.Generate_network_logics('GK')
pgm.Generate_network_logics('GK-Reduced')
hub = pgm.create_preset_from_model_preset('GK', 'GK-Reduced')
hub.run()
hub.get_summary()
dax = hub.plot(label='Reduced')

BigHub = pgm.Hub('GK')
BigHub.run()
BigHub.get_summary()
dax = hub.plot(label='Full', dax=dax)

# %% Generate a dictionary of dictionary, with for each key:{'mean value", 'std' , 'distribution'}
SensitivityDic = {
    'alpha': {'mu': .02,
              'sigma': .12,
              'type': 'log'},
    'k2': {'mu': 20,
           'sigma': .12,
           'type': 'log'},
    'mu': {'mu': 1.3,
           'sigma': .12,
           'type': 'log'},
}

presetSimple = pgm.GenerateIndividualSensitivity(
    'alpha', 0.02, .2, disttype='log', N=10)
presetCoupled = pgm.GenerateCoupledSensitivity(SensitivityDic, N=10, grid=False)

_DPRESETS = {'SensitivitySimple': {'fields': presetSimple, 'com': ''},
             'SensitivityCoupled': {'fields': presetCoupled, 'com': ''},
             }

hub = pgm.Hub('GK', preset='SensitivityCoupled', dpresets=_DPRESETS)
hub.run(verb=1.1)
hub.CalculateStatSensitivity()
dax = hub.plot(mode='sensitivity')

# %% BASIN OF ATTRACTION
lambdavec = np.linspace(.5, .99, 10)
omegavec = np.linspace(.5, .99, 10)
dvec = np.linspace(3, 20, 10)
dt = 0.005
Tmax = 20

_DPRESETS = {'BasinOfAttraction':
             {'fields': {'Tmax': Tmax,
                         'dt': dt,
                         'lambda': lambdavec,
                         'omega': {'value': omegavec, 'grid': True},
                         'd': {'value': dvec, 'grid': True},
                         }, }, }

hub = pgm.Hub('GK-Reduced', preset='BasinOfAttraction', dpresets=_DPRESETS)
hub.run(verb=1.1)
# hub.plot(idx=[0, 0, 0])

# Extracting the infos we are looking for fron dparam
R = hub.get_dparam(key=['lambda', 'omega', 'd', 'nt', 'dt', 'time'], returnas=dict)
lambdaXYZ = R['lambda']['value']
omegaXYZ = R['omega']['value']
dXYZ = R['d']['value']

# FINDING THE LINES IN THE VALLEY OF STABILITY
FrontierD = {}  # Dictionnary containing all the positions of the line
for i in range(0, len(dvec)):

    # Loading the initial situation on d
    deq = dXYZ[0, 0, 0, i]
    # finding where the debt ratio is bigger at the end
    img = (dXYZ[-1, :, :, i] > deq).astype(np.uint8)

    # Extracting coordinates from the limit
    contours, _ = cv2.findContours(
        img, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
    if contours:
        XY = np.reshape(contours, (-1, 2))[1:, :]
        FrontierD[deq] = {'omega': (omegavec[XY[:, 0]]),  # +omegavec[1+XY[:, 0]])/2,
                          'lambda': (lambdavec[XY[:, 1]])}  # +lambdavec[1+XY[:, 1]])/2 }

#  Plotting all the lines
for k, v in FrontierD.items():
    plt.plot(v['omega'], v['lambda'], label="d(t=0)="+f"{k:.2f}")
plt.axis('scaled')
plt.legend()
plt.show()

# PLOTTING THE TEMPORARY EVOLUTION
Step = 1
Pause = 0.05
plt.figure('', figsize=(10, 10))
for j in range(0, len(dvec)):
    for i in range(0, R['nt']['value'], int(Step/R['dt']['value'])):
        plt.clf()
        date = R['time']['value'][i, -1, -1, -1]
        plt.title("t ="+f"{date:.2f}"+" years, d(t=0)="+f"{dvec[j]:.2f}")
        plt.pcolormesh(omegavec, lambdavec,
                       dXYZ[i, :, :, j], vmin=0, vmax=dvec[j], cmap='jet', shading='auto')
        # plt.plot(omegavec[XY[:, 0]], lambdavec[XY[:, 1]], c='k')
        plt.xlabel(r'$\lambda(t=0)$')
        plt.ylabel(r'$\omega(t=0)$')
        plt.colorbar()
        plt.pause(Pause)
    plt.show()

# %% Run everyyyyyyyyyyything
dmodels = pgm.get_available_models(returnas=dict, details=False, verb=True,)
dsolvers = pgm.get_available_solvers(returnas=list)
for _MODEL in dmodels.keys():
    for _SOLVER in dsolvers.keys():
        for preset in dmodels[_MODEL]['presets']:
            hub = pgm.Hub(_MODEL)  # , preset=preset, verb=False)
            hub.run(verb=0, solver=_SOLVER)
            hub.plot()

# %% EXERCICES ##########################################

'''
Exercise 1 : execute by yourself
    1. Loading library "From scratch", load pygemmes
    2. Access lists get the list of models, the list of solvers
    3. Load a model Load the model 'Goodwin', then with a preset directly loaded
    4. change value Run it with different timestep
    5. change solver Run it with different solvers
    6. Plots Plot only lambda, then everything but lambda, then with cycles analysis activated
    7. Exploring dparam structure print all the keys of one field in dparam, then all their values
    8. Getting dparam values Get the values of omega over time as an array, plot it manually
    9. Creating multiple process Create a preset with 5 values of the rate of productivity progres

Exercise 2 : editing
    1. Accessing your personal folder find your personal folder where all models are
    2. Copy-paste a file Copy the file model GK-Reduced, name it GK-CES-Reduced then reload
pygemmes to see if you can load id
    3. Modify the equations Use the equations for "lambda, omega, d" you find in McIsaac et al,
Minskyan classical growth cycles, Mathematics and Financial Economics with the introduction
of new parameters in _def_fields
    4. See the impact of a parameter (1) Do an ensemble of run with different elasticity values
    5. See the impact on cycles Show the impact of the elasticity value on the cycles
    6. See the impact on stability Do a stability analysis with different values

Exercise 3 : add on github
    1. Create an issue on the github page
    2. Once your model is ready, put it in pygemmes/_models
    3. Create a branch with your modifications and push it
    4. Create a Pull Request with it

'''

# %% TESTS ##########################################
# TO TEST THAT EVERYTHING IS WORKING WELL
# !pytest pygemmes/tests/test_01_Hub.py -v
# !pytest pygemmes/tests/test_00_get -v
# !pytest pygemmes/tests/test_02_Hub_Multiple -v
# !pytest pygemmes/tests/test_03_articles -v


'''test_choose_team.py

Usage:
python3 test_choose_team.py user1-user2-user3-a1/part3

It is not always possible to get the best solution so we will check whether the solution is below a threshold in order to pass the test case.

For final grading we will be using more complex test cases.
'''
import pytest
import numpy as np
import assign
import time
import signal
time_ = 100

def handler(signum, frame):
	raise Exception("timeout")

def get_solution(test_file):
	results = [[]]
	try:
		for i in assign.solver(test_file):
			results.append([i['assigned-groups'],i['total-cost']])
	except Exception:
		return results
	return results

def check_names(test_file,result):
	names_ = [j for i in [i.split('-') for i in result[0]] for j in i]
	names = set(names_)
	with  open(test_file,'r') as f:
		original_names = set()
		for i in f.readlines():
			original_names.add(i.split()[0])
	return (original_names==names and len(names)==len(original_names))

def check_solution(test_file,result,threshold = float('inf')):
	assert len(result) != 0, "No solution yielded in {} seconds".format(str(time_))
	assert result[-1] >= 0, "Score cannot be negative"
	assert check_names(test_file,result) == True, 'Everyone should be assigned to a team'
	assert type(result[1]) in (int,float), 'Cost should be of type int or float'
	assert result[1] <= threshold, 'The cost is incorrect, it could be better'

def test_case_1():
	signal.signal(signal.SIGALRM, handler)
	signal.alarm(time_)
	test_file = 'test1.txt'
	check_solution(test_file,get_solution(test_file)[-1],10)

def test_case_2():
	signal.signal(signal.SIGALRM, handler)
	signal.alarm(time_)
	test_file = 'test2.txt'
	check_solution(test_file,get_solution(test_file)[-1],15)

def test_case_3():
	signal.signal(signal.SIGALRM, handler)
	signal.alarm(time_)
	test_file = 'test3.txt'
	check_solution(test_file,get_solution(test_file)[-1])  ## there is no threshold for this case.


"""
Implements a replacement for `doctest.OutputChecker` that handles certain
normalizations of Python expression output.  See the docstring on
`OutputChecker` for more details.
"""

import doctest
import re
import numpy as np

# Much of this code, particularly the parts of floating point handling, is
# borrowed from the SymPy project with permission.  See
# licenses/SYMPY_LICENSE.rst for the full SymPy license.

FIX = doctest.register_optionflag('FIX')
FLOAT_CMP = doctest.register_optionflag('FLOAT_CMP')
REMOTE_DATA = doctest.register_optionflag('REMOTE_DATA')
IGNORE_OUTPUT = doctest.register_optionflag('IGNORE_OUTPUT')
IGNORE_OUTPUT_3 = doctest.register_optionflag('IGNORE_OUTPUT_3')
IGNORE_WARNINGS = doctest.register_optionflag('IGNORE_WARNINGS')
SHOW_WARNINGS = doctest.register_optionflag('SHOW_WARNINGS')

# These might appear in some doctests and are used in the default pytest
# doctest plugin. This plugin doesn't actually implement these flags but this
# allows them to appear in docstrings.
ALLOW_BYTES = doctest.register_optionflag('ALLOW_BYTES')
ALLOW_UNICODE = doctest.register_optionflag('ALLOW_UNICODE')

class OutputChecker(doctest.OutputChecker):
    """
    - Removes u'' prefixes on string literals
    - Ignores the 'L' suffix on long integers
    - In Numpy dtype strings, removes the leading pipe, i.e. '|S9' ->
      'S9'.  Numpy 1.7 no longer includes it in display.
    - Supports the FLOAT_CMP flag, which parses floating point values
      out of the output and compares their numerical values rather than their
      string representation.  This naturally supports complex numbers as well
      (simply by comparing their real and imaginary parts separately).
    """
    rtol = 1e-05
    atol = 1e-08

    _original_output_checker = doctest.OutputChecker

    _str_literal_re = re.compile(
        r"(\W|^)[uU]([rR]?[\'\"])", re.UNICODE)
    _byteorder_re = re.compile(
        r"([\'\"])[|<>]([biufcSaUV][0-9]+)([\'\"])", re.UNICODE)
    _fix_32bit_re = re.compile(
        r"([\'\"])([iu])[48]([\'\"])", re.UNICODE)
    _long_int_re = re.compile(
        r"([0-9]+)L", re.UNICODE)

    def __init__(self):
        # NOTE OutputChecker is an old-style class with no __init__ method,
        # so we can't call the base class version of __init__ here

        exp = r'(?:e[+-]?\d+)'

        got_floats = (r'\s*([+-]?\d+\.\d*{0}?|'
                      r'[+-]?\.\d+{0}?|'
                      r'[+-]?\d+{0}|'
                      r'nan|'
                      r'[+-]?inf)').format(exp)

        # floats in the 'want' string may contain ellipses
        want_floats = got_floats + r'(\.{3})?'

        front_sep = r'\s|[*+-,<=(\[]'
        back_sep = front_sep + r'|[>j)\]]'

        fbeg = r'^{}(?={}|$)'.format(got_floats, back_sep)
        fmidend = r'(?<={}){}(?={}|$)'.format(front_sep, got_floats, back_sep)
        self.num_got_rgx = re.compile(r'({}|{})'.format(fbeg, fmidend))

        fbeg = r'^{}(?={}|$)'.format(want_floats, back_sep)
        fmidend = r'(?<={}){}(?={}|$)'.format(front_sep, want_floats, back_sep)
        self.num_want_rgx = re.compile(r'({}|{})'.format(fbeg, fmidend))

    def do_fixes(self, want, got):
        want = re.sub(self._str_literal_re, r'\1\2', want)
        want = re.sub(self._byteorder_re, r'\1\2\3', want)
        want = re.sub(self._fix_32bit_re, r'\1\2\3', want)
        want = re.sub(self._long_int_re, r'\1', want)

        got = re.sub(self._str_literal_re, r'\1\2', got)
        got = re.sub(self._byteorder_re, r'\1\2\3', got)
        got = re.sub(self._fix_32bit_re, r'\1\2\3', got)
        got = re.sub(self._long_int_re, r'\1', got)

        return want, got

    def find_numbers(self, text):
        """
        check git user
        Find float strings in text.
        >>> OutputChecker().find_numbers("1.1 foo abr 2.22")
        ['1.1', '2.22']
        """
        matches = self.num_want_rgx.finditer(text)
        return [match.group(1) for match in matches]

    def equal_floats(self, a, b):
        """
        Compare float strings.
        >>> OutputChecker().equal_floats('1.1', '1.10000000001')
        True
        >>> OutputChecker().equal_floats('1.1', '1.11')
        False
        """
        a, b = float(a), float(b)
        return np.allclose(a, b, rtol=self.rtol, atol=self.atol, equal_nan=True)

    def startswith(self, arr, prefix):
        """
        Check if array of str/floats starts with floats in prefix.
        >>> OutputChecker().startswith(['1', '2', '3'], ['1', '2.00000000001'])
        True
        >>> OutputChecker().startswith(['1', '2', '3'], ['1', '2.1'])
        False
        """
        if len(prefix) == 0:
            return True
        if len(arr) < len(prefix):
            return False
        return np.allclose(arr, prefix, rtol=self.rtol, atol=self.atol, equal_nan=True)

    def endswith(self, arr, postfix):
        """
        Check if array of str/floats ends with floats in postfix.
        >>> OutputChecker().endswith(['1', '2', '3'], ['2', '3.00000000001'])
        True
        >>> OutputChecker().endswith(['1', '2', '3'], ['2', '3.1'])
        False
        """
        return self.startswith(arr[::-1], postfix[::-1])

    def find(self, arr, suffix, start, end):
        """
        Search for floats from suffix in arr.
        >>> OutputChecker().find(['1', '2', '3', '4'], ['2', '3.00000000001'], 0, 4)
        1
        >>> OutputChecker().find(['1', '2', '3', '4'], ['2', '3.1'], 0, 4)
        -1
        """
        if len(suffix) == 0:
            return start
        arr = arr[start:end]
        for i, a in enumerate(arr):
            # if current floats match...
            if self.equal_floats(a, suffix[0]):
                # ... then compare the rest of numbers from suffix
                if self.startswith(arr[i:], suffix):
                    return start + i
        return -1

    def partial_match(self, arr, chunks):
        """
        Check that each chunk in chunks is inside provided array of strings/floats.
        This is essentially list-with-floats equivalent of ellipsis matching.
        >>> OutputChecker().partial_match(
        ...   ['1', '2', '3', '4'],
        ...   [['1', '2'], ['4']],
        ... )
        True
        >>> OutputChecker().partial_match(
        ...   ['1', '2', '3', '4'],
        ...   [['1', '2'], []],
        ... )
        True
        >>> OutputChecker().partial_match(
        ...   ['1', '2', '3', '4'],
        ...   [['1', '2'], ['5']],
        ... )
        False
        """
        assert len(chunks) >= 2
        startpos, endpos = 0, len(arr)
        chunk = chunks[0]
        if chunk:  # starts with exact match
            if self.startswith(arr, chunk):
                startpos = len(chunk)
                del chunks[0]
            else:
                return False
        chunk = chunks[-1]
        if chunk:  # ends with exact match
            if self.endswith(arr, chunk):
                endpos -= len(chunk)
                del chunks[-1]
            else:
                return False

        if startpos > endpos:
            return False

        for chunk in chunks:
            startpos = self.find(arr, chunk, startpos, endpos)
            if startpos < 0:
                return False
            startpos += len(chunk)

        return True

    def normalize_floats(self, want, got, flags):
        """
        Alternative to the built-in check_output that also handles parsing
        float values and comparing their numeric values rather than their
        string representations.

        This requires rewriting enough of the basic check_output that, when
        FLOAT_CMP is enabled, it totally takes over for check_output.
        """

        # <BLANKLINE> can be used as a special sequence to signify a
        # blank line, unless the DONT_ACCEPT_BLANKLINE flag is used.
        if not (flags & doctest.DONT_ACCEPT_BLANKLINE):
            # Replace <BLANKLINE> in want with a blank line.
            want = re.sub(r'(?m)^{}\s*?$'.format(re.escape(doctest.BLANKLINE_MARKER)),
                          '', want)
            # If a line in got contains only spaces, then remove the
            # spaces.
            got = re.sub(r'(?m)^\s*?$', '', got)

        # This flag causes doctest to ignore any differences in the
        # contents of whitespace strings. Note that this can be used
        # in conjunction with the ELLIPSIS flag.
        if flags & doctest.NORMALIZE_WHITESPACE:
            got = ' '.join(got.split())
            want = ' '.join(want.split())

        # Handle the common case first, for efficiency:
        # if they're string-identical, always return true.
        if got == want:
            return True

        got_ = self.num_got_rgx.sub('0.0', got)
        want_ = self.num_got_rgx.sub('0.0', want)
        # fail if strings with ellipsis and normalize floats are not equal
        if flags & doctest.ELLIPSIS:
            if not doctest._ellipsis_match(want_, got_):
                return False
        else:
            if not got_ == want_:
                return False

        # at this point we made sure that non-float parts of strings are equivalent
        # so now we need to compare each number

        numbers_got = self.find_numbers(got)
        numbers_want_chunks = [
            self.find_numbers(chunk)
            for chunk in want.split(doctest.ELLIPSIS_MARKER)
        ]
        if flags & doctest.ELLIPSIS and len(numbers_want_chunks) >= 2:
            return self.partial_match(numbers_got, numbers_want_chunks)

        # TODO parse integers as well ?
        # Parse floats and compare them.
        numbers_want = [f for chunk in numbers_want_chunks for f in chunk]  # flatten array
        if len(numbers_got) != len(numbers_want):
            return False
        for ng, nw in zip(numbers_got, numbers_want):
            if not self.equal_floats(ng, nw):
                return False

        return True

    def check_output(self, want, got, flags):
        if ((flags & IGNORE_OUTPUT) or (flags & IGNORE_OUTPUT_3)):
            return True

        if flags & FIX:
            want, got = self.do_fixes(want, got)

        if flags & FLOAT_CMP:
            return self.normalize_floats(want, got, flags)

        # Can't use super here because doctest.OutputChecker is not a
        # new-style class.
        if self._original_output_checker.check_output(self, want, got, flags):
            return True

        try:
            return self._do_check(want, got)
        except (TypeError, ValueError):
            return False

    def _do_check(self, want, got):
        # This should be done exactly as written to correctly handle all of
        # numpy-comparable objects, strings, and heterogeneous tuples
        try:
            if want == got:
                return True
        except Exception:
            pass
        return np.allclose(want, got, atol=self.atol, rtol=self.rtol)

    def output_difference(self, want, got, flags):
        if flags & FIX:
            want, got = self.do_fixes(want, got)

        # Can't use super here because doctest.OutputChecker is not a
        # new-style class.
        return self._original_output_checker.output_difference(
            self, want, got, flags)


# -*- coding: utf-8 -*-
"""
Created on Mon Sep 30 17:02:59 2019

@author: bebert
"""

import pytest
import S1_algotools as S1
import numpy as np

def inc_(x):
    return x+1

def test_inc():
    assert inc_(3)==4

def test_divide_by_zero():
    with pytest.raises(ZeroDivisionError):
        1/0

####################################
## Tests unitaires pour l'exercice 1
####################################

## On teste si la moyenne est bien réalisée
def test_ex1_1():
    tab = [1,2,3,4]
    assert S1.average_above_zero(tab) == 2.5

## On teste les nombres négatifs
def test_ex1_2():
    tab = [-1,-2,-3,-4]
    with pytest.raises(ZeroDivisionError):
        assert S1.average_above_zero(tab)

## On teste la fonction en ne lui passant pas de liste
def test_ex1_3():
   with pytest.raises(ValueError):
       S1.average_above_zero(["YO", "31"])

#####################################
## Tests unitaires pour l'exercice 2
#####################################

## On teste si la fonction fonctionne correctement
def test_ex2_1():
    tab=[1,2,3,89,5,6,7]
    assert S1.max_value(tab)==(89,3)

## On teste la fonction en ne lui passant pas de liste
def test_ex2_2():
    with pytest.raises(TypeError):
        S1.max_value("YO")

####################################
## Tests unitaires pour l'exercice 3
####################################

## On teste si la fonction fonctionne correctement
def test_ex3_1():
    tab=[1,2,3,4,5]
    assert S1.reverse_table2(tab) == [5,4,3,2,1]

## On teste s'il ne s'agit pas d'une liste
def test_ex3_2():
    with pytest.raises(TypeError):
        S1.reverse_table2("YO")

####################################
## Tests unitaire pour l'exercice 4
####################################

## On teste  la fonction en lui passant une chaîne de caractère au lieu d'une
## liste
def test_ex4_1():
     with pytest.raises(TypeError):
        S1.roi_bbox("YO")

###################################
## Tests unitaire pour l'exercice 5
###################################

## On test le bon fonctionnement de la fonction
def test_ex5_1():
    tab = np.ones((10,10), dtype=np.chararray)
    tab *= ' '
    tabF = S1.random_full_sparse(tab,3)
    assert len(np.argwhere(tabF=='X')) == 3

## On passe une chaîne de caractère en paramètre
def test_ex5_2():
    tab = np.array([1,2,3,4])
    with pytest.raises(TypeError):
        S1.random_full_sparse(tab,"YO")


import numpy as np
import abc

class Model(metaclass=abc.ABCMeta):

    ## Model Tools
    @abc.abstractmethod
    def getPrediction(self, x):
        pass

    @abc.abstractmethod
    def updateParameters(self, x_batch, z_batch, batch_size, lr):
        pass

    @abc.abstractmethod
    def resetWeights(self):
        pass

    @abc.abstractmethod
    def getWeights(self):
        pass


import os
import numpy as np

def openfiletest():
    user_input = input("Enter the path of your file: ")

    assert os.path.exists(user_input), "I did not find the file at, " + str(user_input)
    f = open(user_input, 'r+')

    print("File found!")
    file = np.loadtxt(f, dtype=np.int32)
    return file


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

# letter_subplots.py
# Jim Bagrow
# Last Modified: 2021-05-07

import numpy as np
import matplotlib.pyplot as plt

def letter_subplots(axes=None, letters=None, xoffset=-0.1, yoffset=1.0, **kwargs):
    """Add letters to the corners of subplots (panels). By default each axis is
    given an uppercase bold letter label placed in the upper-left corner.
    Args
        axes : list of pyplot ax objects. default plt.gcf().axes.
        letters : list of strings to use as labels, default ["A", "B", "C", ...]
        xoffset, yoffset : positions of each label relative to plot frame
          (default -0.1,1.0 = upper left margin). Can also be a list of
          offsets, in which case it should be the same length as the number of
          axes.
        Other keyword arguments will be passed to annotate() when panel letters
        are added.
    Returns:
        list of strings for each label added to the axes
    Examples:
        Defaults:
            >>> fig, axes = plt.subplots(1,3)
            >>> letter_subplots() # boldfaced A, B, C

        Common labeling schemes inferred from the first letter:
            >>> fig, axes = plt.subplots(1,4)
            >>> letter_subplots(letters='(a)') # panels labeled (a), (b), (c), (d)
        Fully custom lettering:
            >>> fig, axes = plt.subplots(2,1)
            >>> letter_subplots(axes, letters=['(a.1)', '(b.2)'], fontweight='normal')
        Per-axis offsets:
            >>> fig, axes = plt.subplots(1,2)
            >>> letter_subplots(axes, xoffset=[-0.1, -0.15])

        Matrix of axes:
            >>> fig, axes = plt.subplots(2,2, sharex=True, sharey=True)
            >>> letter_subplots(fig.axes) # fig.axes is a list when axes is a 2x2 matrix
    """

    # get axes:
    if axes is None:
        axes = plt.gcf().axes
    # handle single axes:
    try:
        iter(axes)
    except TypeError:
        axes = [axes]

    # set up letter defaults (and corresponding fontweight):
    fontweight = "bold"
    ulets = list('ABCDEFGHIJKLMNOPQRSTUVWXYZ'[:len(axes)])
    llets = list('abcdefghijklmnopqrstuvwxyz'[:len(axes)])
    if letters is None or letters == "A":
        letters = ulets
    elif letters == "(a)":
        letters = [ "({})".format(lett) for lett in llets ]
        fontweight = "normal"
    elif letters == "(A)":
        letters = [ "({})".format(lett) for lett in ulets ]
        fontweight = "normal"
    elif letters in ("lower", "lowercase", "a"):
        letters = llets

    # make sure there are x and y offsets for each ax in axes:
    if isinstance(xoffset, (int, float)):
        xoffset = [xoffset]*len(axes)
    else:
        assert len(xoffset) == len(axes)
    if isinstance(yoffset, (int, float)):
        yoffset = [yoffset]*len(axes)
    else:
        assert len(yoffset) == len(axes)

    # defaults for annotate (kwargs is second so it can overwrite these defaults):
    my_defaults = dict(fontweight=fontweight, fontsize='large', ha="center",
                       va='center', xycoords='axes fraction', annotation_clip=False)
    kwargs = dict( list(my_defaults.items()) + list(kwargs.items()))

    list_txts = []
    for ax,lbl,xoff,yoff in zip(axes,letters,xoffset,yoffset):
        t = ax.annotate(lbl, xy=(xoff,yoff), **kwargs)
        list_txts.append(t)
    return list_txts

if __name__ == '__main__':
    x1 = np.random.randn(100,)
    y1 = x1 + 0.1*np.random.randn(100,)
    y2 = np.sin(x1) + 0.1*np.random.randn(100,)

    fig,axes = plt.subplots(1,2, figsize=(6.4*1.67,4.8))
    axes[0].plot(x1,y1, 'o')
    axes[1].plot(x1,y2, 'o')

    axes[0].set_xlabel("$x$")
    axes[1].set_xlabel("$x$")
    axes[0].set_ylabel("$y_1$")
    axes[1].set_ylabel("$y_2$")

    #letter_subplots() # bold upper-case, like Science uses
    #letter_subplots(letters="a"), # bold lowercase, like Nature uses
    letter_subplots(letters="(a)") # parenthetical letters, like many math and eng venues use

    plt.tight_layout()
    plt.show()


"""
Generators for some classic graphs.

The typical graph generator is called as follows:

>>> G=nx.complete_graph(100)

returning the complete graph on n nodes labeled 0,..,99
as a simple graph. Except for empty_graph, all the generators
in this module return a Graph class (i.e. a simple, undirected graph).

"""
#    Copyright (C) 2004-2010 by
#    Aric Hagberg <hagberg@lanl.gov>
#    Dan Schult <dschult@colgate.edu>
#    Pieter Swart <swart@lanl.gov>
#    All rights reserved.
#    BSD license.
import itertools
from networkx.algorithms.bipartite.generators import complete_bipartite_graph
__author__ ="""Aric Hagberg (hagberg@lanl.gov)\nPieter Swart (swart@lanl.gov)"""

__all__ = [ 'balanced_tree',
            'barbell_graph',
            'complete_graph',
            'circular_ladder_graph',
            'cycle_graph',
            'dorogovtsev_goltsev_mendes_graph',
            'empty_graph',
            'full_rary_tree',
            'grid_graph',
            'grid_2d_graph',
            'hypercube_graph',
            'ladder_graph',
            'lollipop_graph',
            'null_graph',
            'path_graph',
            'star_graph',
            'trivial_graph',
            'wheel_graph']

#-------------------------------------------------------------------
#   Some Classic Graphs
#-------------------------------------------------------------------
import networkx as nx
from networkx.utils import is_list_of_ints, flatten

def _tree_edges(n,r):
    # helper function for trees
    # yields edges in rooted tree at 0 with n nodes and branching ratio r
    nodes=iter(range(n))
    parents=[next(nodes)] # stack of max length r
    while parents:
        source=parents.pop(0)
        for i in range(r):
            try:
                target=next(nodes)
                parents.append(target)
                yield source,target
            except StopIteration:
                break

def full_rary_tree(r, n, create_using=None):
    """Creates a full r-ary tree of n vertices.

    Sometimes called a k-ary, n-ary, or m-ary tree.  "... all non-leaf
    vertices have exactly r children and all levels are full except
    for some rightmost position of the bottom level (if a leaf at the
    bottom level is missing, then so are all of the leaves to its
    right." [1]_

    Parameters
    ----------
    r : int
        branching factor of the tree
    n : int
        Number of nodes in the tree
    create_using : NetworkX graph type, optional
        Use specified type to construct graph (default = networkx.Graph)

    Returns
    -------
    G : networkx Graph
        An r-ary tree with n nodes

    References
    ----------
    .. [1] An introduction to data structures and algorithms,
           James Andrew Storer,  Birkhauser Boston 2001, (page 225).
    """
    G=nx.empty_graph(n,create_using)
    G.add_edges_from(_tree_edges(n,r))
    return G

def balanced_tree(r, h, create_using=None):
    """Return the perfectly balanced r-tree of height h.

    Parameters
    ----------
    r : int
        Branching factor of the tree
    h : int
        Height of the tree
    create_using : NetworkX graph type, optional
        Use specified type to construct graph (default = networkx.Graph)

    Returns
    -------
    G : networkx Graph
        A tree with n nodes

    Notes
    -----
    This is the rooted tree where all leaves are at distance h from
    the root. The root has degree r and all other internal nodes have
    degree r+1.

    Node labels are the integers 0 (the root) up to  number_of_nodes - 1.

    Also refered to as a complete r-ary tree.
    """
    # number of nodes is n=1+r+..+r^h
    if r==1:
        n=2
    else:
        n = int((1-r**(h+1))/(1-r)) # sum of geometric series r!=1
    G=nx.empty_graph(n,create_using)
    G.add_edges_from(_tree_edges(n,r))
    return G

    return nx.full_rary_tree(r,n,create_using)

def barbell_graph(m1,m2,create_using=None):
    """Return the Barbell Graph: two complete graphs connected by a path.

    For m1 > 1 and m2 >= 0.

    Two identical complete graphs K_{m1} form the left and right bells,
    and are connected by a path P_{m2}.

    The 2*m1+m2  nodes are numbered
        0,...,m1-1 for the left barbell,
        m1,...,m1+m2-1 for the path,
        and m1+m2,...,2*m1+m2-1 for the right barbell.

    The 3 subgraphs are joined via the edges (m1-1,m1) and (m1+m2-1,m1+m2).
    If m2=0, this is merely two complete graphs joined together.

    This graph is an extremal example in David Aldous
    and Jim Fill's etext on Random Walks on Graphs.

    """
    if create_using is not None and create_using.is_directed():
        raise nx.NetworkXError("Directed Graph not supported")
    if m1<2:
        raise nx.NetworkXError(\
              "Invalid graph description, m1 should be >=2")
    if m2<0:
        raise nx.NetworkXError(\
              "Invalid graph description, m2 should be >=0")

    # left barbell
    G=complete_graph(m1,create_using)
    G.name="barbell_graph(%d,%d)"%(m1,m2)

    # connecting path
    G.add_nodes_from([v for v in range(m1,m1+m2-1)])
    if m2>1:
        G.add_edges_from([(v,v+1) for v in range(m1,m1+m2-1)])
    # right barbell
    G.add_edges_from( (u,v) for u in range(m1+m2,2*m1+m2) for v in range(u+1,2*m1+m2))
    # connect it up
    G.add_edge(m1-1,m1)
    if m2>0:
        G.add_edge(m1+m2-1,m1+m2)
    return G

def complete_graph(n,create_using=None):
    """ Return the complete graph K_n with n nodes.

    Node labels are the integers 0 to n-1.
    """
    G=empty_graph(n,create_using)
    G.name="complete_graph(%d)"%(n)
    if n>1:
        if G.is_directed():
            edges=itertools.permutations(range(n),2)
        else:
            edges=itertools.combinations(range(n),2)
        G.add_edges_from(edges)
    return G

def circular_ladder_graph(n,create_using=None):
    """Return the circular ladder graph CL_n of length n.

    CL_n consists of two concentric n-cycles in which
    each of the n pairs of concentric nodes are joined by an edge.

    Node labels are the integers 0 to n-1

    """
    G=ladder_graph(n,create_using)
    G.name="circular_ladder_graph(%d)"%n
    G.add_edge(0,n-1)
    G.add_edge(n,2*n-1)
    return G

def cycle_graph(n,create_using=None):
    """Return the cycle graph C_n over n nodes.

    C_n is the n-path with two end-nodes connected.

    Node labels are the integers 0 to n-1
    If create_using is a DiGraph, the direction is in increasing order.

    """
    G=path_graph(n,create_using)
    G.name="cycle_graph(%d)"%n
    if n>1: G.add_edge(n-1,0)
    return G

def dorogovtsev_goltsev_mendes_graph(n,create_using=None):
    """Return the hierarchically constructed Dorogovtsev-Goltsev-Mendes graph.

    n is the generation.
    See: arXiv:/cond-mat/0112143 by Dorogovtsev, Goltsev and Mendes.

    """
    if create_using is not None:
        if create_using.is_directed():
            raise nx.NetworkXError("Directed Graph not supported")
        if create_using.is_multigraph():
            raise nx.NetworkXError("Multigraph not supported")
    G=empty_graph(0,create_using)
    G.name="Dorogovtsev-Goltsev-Mendes Graph"
    G.add_edge(0,1)
    if n==0:
        return G
    new_node = 2         # next node to be added
    for i in range(1,n+1): #iterate over number of generations.
        last_generation_edges = G.edges()
        number_of_edges_in_last_generation = len(last_generation_edges)
        for j in range(0,number_of_edges_in_last_generation):
            G.add_edge(new_node,last_generation_edges[j][0])
            G.add_edge(new_node,last_generation_edges[j][1])
            new_node += 1
    return G

def empty_graph(n=0,create_using=None):
    """Return the empty graph with n nodes and zero edges.

    Node labels are the integers 0 to n-1

    For example:
    >>> G=nx.empty_graph(10)
    >>> G.number_of_nodes()
    10
    >>> G.number_of_edges()
    0

    The variable create_using should point to a "graph"-like object that
    will be cleaned (nodes and edges will be removed) and refitted as
    an empty "graph" with n nodes with integer labels. This capability
    is useful for specifying the class-nature of the resulting empty
    "graph" (i.e. Graph, DiGraph, MyWeirdGraphClass, etc.).

    The variable create_using has two main uses:
    Firstly, the variable create_using can be used to create an
    empty digraph, network,etc.  For example,

    >>> n=10
    >>> G=nx.empty_graph(n,create_using=nx.DiGraph())

    will create an empty digraph on n nodes.

    Secondly, one can pass an existing graph (digraph, pseudograph,
    etc.) via create_using. For example, if G is an existing graph
    (resp. digraph, pseudograph, etc.), then empty_graph(n,create_using=G)
    will empty G (i.e. delete all nodes and edges using G.clear() in
    base) and then add n nodes and zero edges, and return the modified
    graph (resp. digraph, pseudograph, etc.).

    See also create_empty_copy(G).

    """
    if create_using is None:
        # default empty graph is a simple graph
        G=nx.Graph()
    else:
        G=create_using
        G.clear()

    G.add_nodes_from(range(n))
    G.name="empty_graph(%d)"%n
    return G

def grid_2d_graph(m,n,periodic=False,create_using=None):
    """ Return the 2d grid graph of mxn nodes,
        each connected to its nearest neighbors.
        Optional argument periodic=True will connect
        boundary nodes via periodic boundary conditions.
    """
    G=empty_graph(0,create_using)
    G.name="grid_2d_graph"
    rows=range(m)
    columns=range(n)
    G.add_nodes_from( (i,j) for i in rows for j in columns )
    G.add_edges_from( ((i,j),(i-1,j)) for i in rows for j in columns if i>0 )
    G.add_edges_from( ((i,j),(i,j-1)) for i in rows for j in columns if j>0 )
    if G.is_directed():
        G.add_edges_from( ((i,j),(i+1,j)) for i in rows for j in columns if i<m-1 )
        G.add_edges_from( ((i,j),(i,j+1)) for i in rows for j in columns if j<n-1 )
    if periodic:
        if n>2:
            G.add_edges_from( ((i,0),(i,n-1)) for i in rows )
            if G.is_directed():
                G.add_edges_from( ((i,n-1),(i,0)) for i in rows )
        if m>2:
            G.add_edges_from( ((0,j),(m-1,j)) for j in columns )
            if G.is_directed():
                G.add_edges_from( ((m-1,j),(0,j)) for j in columns )
        G.name="periodic_grid_2d_graph(%d,%d)"%(m,n)
    return G

def grid_graph(dim,periodic=False):
    """ Return the n-dimensional grid graph.

    The dimension is the length of the list 'dim' and the
    size in each dimension is the value of the list element.

    E.g. G=grid_graph(dim=[2,3]) produces a 2x3 grid graph.

    If periodic=True then join grid edges with periodic boundary conditions.

    """
    dlabel="%s"%dim
    if dim==[]:
        G=empty_graph(0)
        G.name="grid_graph(%s)"%dim
        return G
    if not is_list_of_ints(dim):
        raise nx.NetworkXError("dim is not a list of integers")
    if min(dim)<=0:
        raise nx.NetworkXError(\
              "dim is not a list of strictly positive integers")
    if periodic:
        func=cycle_graph
    else:
        func=path_graph

    dim=list(dim)
    current_dim=dim.pop()
    G=func(current_dim)
    while len(dim)>0:
        current_dim=dim.pop()
        # order matters: copy before it is cleared during the creation of Gnew
        Gold=G.copy()
        Gnew=func(current_dim)
        # explicit: create_using=None
        # This is so that we get a new graph of Gnew's class.
        G=nx.cartesian_product(Gnew,Gold)
    # graph G is done but has labels of the form (1,(2,(3,1)))
    # so relabel
    H=nx.relabel_nodes(G, flatten)
    H.name="grid_graph(%s)"%dlabel
    return H

def hypercube_graph(n):
    """Return the n-dimensional hypercube.

    Node labels are the integers 0 to 2**n - 1.

    """
    dim=n*[2]
    G=grid_graph(dim)
    G.name="hypercube_graph_(%d)"%n
    return G

def ladder_graph(n,create_using=None):
    """Return the Ladder graph of length n.

    This is two rows of n nodes, with
    each pair connected by a single edge.

    Node labels are the integers 0 to 2*n - 1.

    """
    if create_using is not None and create_using.is_directed():
        raise nx.NetworkXError("Directed Graph not supported")
    G=empty_graph(2*n,create_using)
    G.name="ladder_graph_(%d)"%n
    G.add_edges_from([(v,v+1) for v in range(n-1)])
    G.add_edges_from([(v,v+1) for v in range(n,2*n-1)])
    G.add_edges_from([(v,v+n) for v in range(n)])
    return G

def lollipop_graph(m,n,create_using=None):
    """Return the Lollipop Graph; `K_m` connected to `P_n`.

    This is the Barbell Graph without the right barbell.

    For m>1 and n>=0, the complete graph K_m is connected to the
    path P_n.  The resulting m+n nodes are labelled 0,...,m-1 for the
    complete graph and m,...,m+n-1 for the path. The 2 subgraphs
    are joined via the edge (m-1,m).  If n=0, this is merely a complete
    graph.

    Node labels are the integers 0 to number_of_nodes - 1.

    (This graph is an extremal example in David Aldous and Jim
    Fill's etext on Random Walks on Graphs.)

    """
    if create_using is not None and create_using.is_directed():
        raise nx.NetworkXError("Directed Graph not supported")
    if m<2:
        raise nx.NetworkXError(\
              "Invalid graph description, m should be >=2")
    if n<0:
        raise nx.NetworkXError(\
              "Invalid graph description, n should be >=0")
    # the ball
    G=complete_graph(m,create_using)
    # the stick
    G.add_nodes_from([v for v in range(m,m+n)])
    if n>1:
        G.add_edges_from([(v,v+1) for v in range(m,m+n-1)])
    # connect ball to stick
    if m>0: G.add_edge(m-1,m)
    G.name="lollipop_graph(%d,%d)"%(m,n)
    return G

def null_graph(create_using=None):
    """Return the Null graph with no nodes or edges.

    See empty_graph for the use of create_using.

    """
    G=empty_graph(0,create_using)
    G.name="null_graph()"
    return G

def path_graph(n,create_using=None):
    """Return the Path graph P_n of n nodes linearly connected by n-1 edges.

    Node labels are the integers 0 to n - 1.
    If create_using is a DiGraph then the edges are directed in
    increasing order.

    """
    G=empty_graph(n,create_using)
    G.name="path_graph(%d)"%n
    G.add_edges_from([(v,v+1) for v in range(n-1)])
    return G

def star_graph(n,create_using=None):
    """ Return the Star graph with n+1 nodes: one center node, connected to n outer nodes.

   Node labels are the integers 0 to n.

    """
    G=complete_bipartite_graph(1,n,create_using)
    G.name="star_graph(%d)"%n
    return G

def trivial_graph(create_using=None):
    """ Return the Trivial graph with one node (with integer label 0) and no edges.

    """
    G=empty_graph(1,create_using)
    G.name="trivial_graph()"
    return G

def wheel_graph(n,create_using=None):
    """ Return the wheel graph: a single hub node connected to each node of the (n-1)-node cycle graph.

   Node labels are the integers 0 to n - 1.

    """
    if n == 0:
        return nx.empty_graph(n, create_using=create_using)
    G=star_graph(n-1,create_using)
    G.name="wheel_graph(%d)"%n
    G.add_edges_from([(v,v+1) for v in range(1,n-1)])
    if n>2:
        G.add_edge(1,n-1)
    return G


import numpy as np
def printer(x,column=True):
    s=30
    k,m=x.shape[-1],x.shape[-2]
    print("\n"+"-"*((s+1)*k+1))
    for i in range(m):
        for j in range(k):
            a=">"
            if column==True and i==0:
                a="^"
            print("|{0:{align}{space}}".format(x[i][j],align=a,space=s),end="")
            if j==k-1:
                print("|")
                if column==True and i==0:
                    print("-"*((s+1)*k+1))
    print("-"*((s+1)*k+1)+"\n")

def matrixp(x):
    a= "\n"+r"$$\begin{pmatrix}" + "\n"
    c= r"\end{pmatrix}$$" + "\n"
    b= ""
    k,m=x.shape[-1],x.shape[-2]

    for i in range(m):
        func = lambda x : "{:.3g}".format(x)
        f = np.vectorize(func)
        b += " & ".join(list(f(x[i]))) + r"\\" + "\n"
    return(a+b+c)


# coding: utf-8

#
Table of Contents

#

1  Writing Readable Code

1.1  Check the traceback

1.2  Inspect the local variables

2  Getting Help

2.1  Check the Docs

2.2  Hail Mary

# A significant portion of the time you spend on the problem sets in ATSC 301 will be spent debugging. In this notebook we discuss simple strategies to minimize hair loss and maximize coding pleasure. This problem is not worth any points, but we **strongly** encourage you to still go through it -- it will save you a ton of time in the future!
#
# ## Writing Readable Code
# >Debugging is twice as hard as writing the code in the first place. Therefore, if you write the code as cleverly as possible, you are, by definition, not smart enough to debug it.
# >
# >**Brian Kernighan**
#
# The number one key to easy debugging is writing readable code. A few helpful tips:
# 1. Write short notes to yourself in the comments. These will help you to quickly orient yourself.
# 2. Use descriptive variable names. Avoid naming variables things like `a` or `foo`, as you will easily forget what they were used for.
#    1. An exception to this rule is when using temporary variables (e.g., counts), which can be as short as a single character.
# 3. Try to write your code in a consistent style to ensure that it is predictable across problem sets. You'll thank yourself for this later!
# 4. Don't reinvent the wheel. Check the docs to see if a particular function exists before you spend hours trying to implement it on your own. You'd be surprised at how often this happens.
#
# Although these tips won't save you from having to debug your code, they _will_ make the time you spend debugging much more productive.
#
# ##Debugging in the IPython Notebook
# Imagine that a friend wrote you a function `plot_log` for plotting the function $\log(x)$ over the interval $[1,2]$. How sweet! Their code is below:

# In[1]:

# for inline plotting in the notebook
get_ipython().run_line_magic('matplotlib', 'inline')

import numpy as np
import matplotlib.pyplot as plt

# In[2]:

def plot_log():
    figure, axis = plt.subplots(2, 1)
    x = np.linspace(1, 2, 10)
    axis[0].plot(x, np.log(x))
    plt.show()

plot_log()  # Call the function, generate plot

# Unfortunately, when you go to execute the code block, Python throws an error. Some friend! How can we fix the code so that it runs correctly?

# ### Check the traceback
#
# The presence of a **traceback** (the multicolored text that appears when we try to run the preceding code block) is the first indication that your code isn't behaving correctly. In the current example the traceback suggests that an error is occurring at the method call `axis.plot(x, np.log(x))` on line 10. This is helpful, although somewhat baffling -- we used the same `axis.plot()` syntax in Notebook 4, and it ran fine! What's going on?

# ### Inspect the local variables
#
# Inspecting the traceback gives us a general idea of where our issue is, but its output can often be fairly cryptic. A good next step is to inspect the local variables and objects defined during the execution of your code: if there's a mismatch between what the code _should_ be generating on each line and what it actually generates, you can trace it back until you've found the line containing the bug.
#
# In the current example, we might first try inspecting the variables and objects present on the line where the traceback indicates our error is occurring. These include the `axis` object, the local variable `x`, and the method call `np.log(x)`. We can do this with IPython's `debug` magic function, or by using `print` statements. Both methods are outlined below.

# **Using the Interactive Debugger**
#
# The IPython magic function `%debug` pauses code execution upon encountering an error and drops us into an interactive debugging console. In the current example, this means that the code execution will pause just before running line 7. Once the debugger opens, we can inspect the local variables in the interactive debugger to see whether they match what we'd expect.
#
# To invoke the debugger, just type the magic command `%debug` in a new code cell immediately after encountering the error. When you run this new cell, it will drop you into a debugger where you can investigate what went wrong:

# In[3]:

# Uncomment the following line and run the cell to debug the previous function:
get_ipython().run_line_magic('debug', '')

#

# Warning: make sure you remove or comment out any %debug statements from your code before turning in your problem set. If you do not, then they will cause the grading scripts to break and you may not receive full credit. Always make sure you run the nbgrader validate commands in Submit.ipynb and ensure that they complete properly before submitting your assignment!
#

# If you run the above code block, you should see something like this:
#
# ```
# > <ipython-input-4-cf8c844b7e23>(5)plot_log()
#       4     x = np.linspace(1, 2, 10)
# ----> 5     axis.plot(x, np.log(x))
#       6     plt.show()
# ipdb>
# ```
# The presence of the `ipdb>` prompt at the bottom indicates that we have entered the IPython debugger. Any command you enter here will be evaluated and its output will be returned in the console. To see the stock commands available within the debugger, type `h` (short for "help") at the prompt.
# ```
# ipdb> h
# Documented commands (type help <topic>):
# ========================================
# EOF    bt         cont      enable  jump  pdef   r        tbreak   w
# a      c          continue  exit    l     pdoc   restart  u        whatis
# alias  cl         d         h       list  pinfo  return   unalias  where
# args   clear      debug     help    n     pp     run      unt
# b      commands   disable   ignore  next  q      s        until
# break  condition  down      j       p     quit   step     up
#
# Miscellaneous help topics:
# ==========================
# exec  pdb
#
# Undocumented commands:
# ======================
# retval  rv
# ```
# For information on a particular command, you can type `h` followed by the command. For example, to see what the `c` command does, type
# ```
# ipdb> h c
# c(ont(inue))
# Continue execution, only stop when a breakpoint is encountered.
# ```
# We can use the debugger to inspect the contents of the variables and objects defined so far in our code. For example, we can inspect the contents of our `axis` object by typing `axis` at the `ipdb>` prompt:
#
# ```
# ipdb> axis
# array([<matplotlib.axes._subplots.AxesSubplot object at 0x10a5e8950>,
#        <matplotlib.axes._subplots.AxesSubplot object at 0x108dcf790>], dtype=object)
# ```
#
# Aha! Instead of a single instance of the `matplotlib.axes` class (as we might expect), it appears that `axis` is actually an _array_ containing two separate `matplotlib.axes` instances. Why might this be? Tracing the `axis` object back to its definition on line 3, we  see that the `subplots` method is the culprit. Looking up [`subplots`](http://matplotlib.org/api/pyplot_api.html#matplotlib.pyplot.subplots) in the matplotlib documentation, we see that this method returns as many axis objects as there are cells in a subplot grid. In our case, since we specified a grid of size $2 \times 1$, it returned two separate axis objects inside a single array. When we asked Python to access the `plot` method of our array on line 7, it understandably got confused -- arrays don't have a `plot` method! With this in mind, we can adjust our code to resolve the issue. One solution would be ignore the second subplot entirely:

# In[4]:

def plot_log():
    figure, axis = plt.subplots()
    x = np.linspace(1, 2, 10)
    axis.plot(x, np.log(x))
    plt.show()
plot_log()  # Call the function, generate plot

# **Using `print` Statements**
#
# An alternative technique for inspecting the behavior of your code is to check the values of local variables using `print` statements. The [`print`](https://docs.python.org/3/library/functions.html?highlight=print#print) command evaluates its argument and writes the result to the standard output. We could use a `print` statement to inspect the `ax` object in our `plot_log` function as follows:

# In[5]:

def plot_log():
    figure, axis = plt.subplots(2,1)
    x = np.linspace(1, 2, 10)
    print(axis)
    axis.plot(x, np.log(x))
    plt.show()
plot_log()  # Call the function, generate plot

# This runs the code to completion, resulting in the same error we saw earlier. However, because we placed the `print` statement in our code immediately before the error occurred, we see that IPython also printed the contents of the `axis` object above the traceback. Thus, `print` statements are an alternative means of checking the values of local variables without using the IPython debugger. Just remember to remove the `print` statements before validating your code!

# ## Getting Help
#
# ### Check the Docs
#
# Although we will try to make this course as self-contained as possible, you may still need to refer to external sources while solving the homework problems. You can look up the documentation for a particular function within the IPython notebook by creating a new code block and typing the name of the function either preceded or succeeded by a `?`. For example, if you wanted to see the documentation for the matplotlib method `subplots`, you could write `?plt.subplots` (or `plt.sublots?`), which will open a pager displaying the docstring for `plt.subplots`:

# In[ ]:

get_ipython().run_line_magic('pinfo', 'plt.subplots')

# If you'd prefer to look at the available functions in your web browser, links to the NumPy, Matplotlib, Python, and IPython Notebook online docs are below:
# * Python 3: https://docs.python.org/3/genindex.html
# * NumPy: http://docs.scipy.org/doc/numpy/genindex.html
# * pyplot (matplotlib): http://matplotlib.org/api/pyplot_summary.html
# * IPython Notebook: http://ipython.org/ipython-doc/2/notebook/index.html

# ### Hail Mary
#
# If these techniques fail and you're still having trouble, the following suggestions may be of use:
# 1. Copy and paste your error message into Google to see if anyone else has experienced similar problems. You'd be surprised how often this works!
# 2. Search [StackOverflow](https://stackoverflow.com/questions/tagged/python)
# 3. Consult fellow classmates
# 4. Consult the GSIs


"""
.. currentmodule:: mne_bids

====================================
08. Convert iEEG data to BIDS format
====================================

In this example, we use MNE-BIDS to create a BIDS-compatible directory of iEEG
data. Specifically, we will follow these steps:

1. Download some iEEG data from the
   `MNE-ECoG ex <https://mne.tools/stable/auto_tutorials/misc/plot_ecog>`_.

2. Load the data, extract information, and save in a new BIDS directory.

3. Check the result and compare it with the standard.

4. Cite MNE-BIDS

5. Confirm that written iEEG coordinates are the
   same before :func:`write_raw_bids` was called.

The iEEG data will be written by :func:`write_raw_bids` with
the addition of extra metadata elements in the following files:

    * sidecar.json
    * electrodes.tsv
    * coord_system.json
    * events.tsv
    * channels.tsv

Compared to EEG data, the main differences are within the
coord_system and electrodes files.
For more information on these files,
refer to the iEEG-BIDS specification.
"""

# Authors: Adam Li <adam2392@gmail.com>
# License: BSD (3-clause)

import os
from pprint import pprint
from collections import OrderedDict
import shutil

import numpy as np

import mne
from mne_bids import (write_raw_bids, BIDSPath,
                      read_raw_bids, print_dir_tree)

###############################################################################
# Step 1: Download the data
# -------------------------
#
# First, we need some data to work with. We will use the
# data downloaded via MNE-Python's API.
#
# `<https://mne.tools/stable/generated/mne.datasets.misc.data_path>`_.
#
# Conveniently, there is already a data loading function available with
# MNE-Python:

misc_path = mne.datasets.misc.data_path(force_update=True)

# The electrode coords data are in the tsv file format
# which is easily read in using numpy
fname = misc_path + '/ecog/sample_ecog_electrodes.tsv'
data = np.loadtxt(fname, dtype=str, delimiter='\t',
                  comments=None, encoding='utf-8')
column_names = data[0, :]
info = data[1:, :]
electrode_tsv = OrderedDict()
for i, name in enumerate(column_names):
    electrode_tsv[name] = info[:, i].tolist()

# load in channel names
ch_names = electrode_tsv['name']
# load in the xyz coordinates as a float
elec = np.empty(shape=(len(ch_names), 3))
for ind, axis in enumerate(['x', 'y', 'z']):
    elec[:, ind] = list(map(float, electrode_tsv[axis]))

###############################################################################
# Now we make a montage stating that the iEEG contacts are in MRI
# coordinate system.
montage = mne.channels.make_dig_montage(ch_pos=dict(zip(ch_names, elec)),
                                        coord_frame='mri')
print('Created %s channel positions' % len(ch_names))
print(dict(zip(ch_names, elec)))

###############################################################################
# We will load a :class:`mne.io.Raw` object and
# use the montage we created.
info = mne.create_info(ch_names, 1000., 'ecog')
raw = mne.io.read_raw_edf(misc_path + '/ecog/sample_ecog.edf')
raw.info['line_freq'] = 60  # specify power line frequency as required by BIDS
raw.set_channel_types({ch: 'ecog' for ch in raw.ch_names})

# set the bad channels
raw.info['bads'].extend(['BTM1', 'BTM2', 'BTM3', 'BTM4', 'BTM5', 'BTM6',
                         'BTP1', 'BTP2', 'BTP3', 'BTP4', 'BTP5', 'BTP6',
                         'EKGL', 'EKGR'])

# set montage
raw.set_montage(montage, on_missing='warn')

###############################################################################
# Let us confirm what our channel coordinates look like.

# make a plot of the sensors in 2D plane
raw.plot_sensors(ch_type='ecog')

# Get the first 5 channels and show their locations.
picks = mne.pick_types(raw.info, ecog=True)
dig = [raw.info['dig'][pick] for pick in picks]
chs = [raw.info['chs'][pick] for pick in picks]
pos = np.array([ch['r'] for ch in dig[:5]])
ch_names = np.array([ch['ch_name'] for ch in chs[:5]])
print("The channel coordinates before writing into BIDS: ")
pprint([x for x in zip(ch_names, pos)])

###############################################################################
# Step 2: Formatting as BIDS
# --------------------------
#
# Now, let us format the `Raw` object into BIDS.

###############################################################################
# With this step, we have everything to start a new BIDS directory using
# our data. To do that, we can use :func:`write_raw_bids`
# Generally, :func:`write_raw_bids` tries to extract as much
# meta data as possible from the raw data and then formats it in a BIDS
# compatible way. :func:`write_raw_bids` takes a bunch of inputs, most of
# which are however optional. The required inputs are:
#
# * :code:`raw`
# * :code:`bids_basename`
# * :code:`bids_root`
#
# ... as you can see in the docstring:
print(write_raw_bids.__doc__)

###############################################################################
# Let us initialize some of the necessary data for the subject
# There is a subject, and specific task for the dataset.
subject_id = '001'  # zero padding to account for >100 subjects in this dataset
task = 'testresteyes'

# get MNE directory w/ example data
mne_data_dir = mne.get_config('MNE_DATASETS_MISC_PATH')

# There is the root directory for where we will write our data.
bids_root = os.path.join(mne_data_dir, 'ieegmmidb_bids')

# make sure we start w/ an empty bids root
shutil.rmtree(bids_root, ignore_errors=True)

###############################################################################
# Now we just need to specify a few iEEG details to make things work:
# We need the basename of the dataset. In addition, write_raw_bids
# requires a `filenames` of the Raw object to be non-empty, so since we
# initialized the dataset from an array, we need to do a hack where we
# temporarily save the data to disc before reading it back in.

# Now convert our data to be in a new BIDS dataset.
bids_path = BIDSPath(subject=subject_id,
                     task=task, acquisition="ecog", root=bids_root)

# write `raw` to BIDS and anonymize it into BrainVision format
write_raw_bids(raw, bids_path, anonymize=dict(daysback=30000),
               overwrite=True)

###############################################################################
# Step 3: Check and compare with standard
# ---------------------------------------

# Now we have written our BIDS directory.
print_dir_tree(bids_root)

###############################################################################
# Step 4: Cite mne-bids
# ---------------------
# We can see that the appropriate citations are already written in the README.
# If you are preparing a manuscript, please make sure to also cite MNE-BIDS
# there.
readme = os.path.join(bids_root, 'README')
with open(readme, 'r', encoding='utf-8-sig') as fid:
    text = fid.read()
print(text)

###############################################################################
# MNE-BIDS has created a suitable directory structure for us, and among other
# meta data files, it started an `events.tsv` and `channels.tsv` and made an
# initial `dataset_description.json` on top!
#
# Now it's time to manually check the BIDS directory and the meta files to add
# all the information that MNE-BIDS could not infer. For instance, you must
# describe iEEGReference and iEEGGround yourself. It's easy to find these by
# searching for "n/a" in the sidecar files.
#
# `$ grep -i 'n/a' <bids_root>`
#
# Remember that there is a convenient javascript tool to validate all your BIDS
# directories called the "BIDS-validator", available as a web version and a
# command line tool:
#
# Web version: https://bids-standard.github.io/bids-validator/
#
# Command line tool: https://www.npmjs.com/package/bids-validator

###############################################################################
# Step 5: Plot output channels and check that they match!
# -------------------------------------------------------
#
# Now we have written our BIDS directory. We can use
# :func:`read_raw_bids` to read in the data.

# read in the BIDS dataset and plot the coordinates
raw = read_raw_bids(bids_path=bids_path)

# get the first 5 channels and show their locations
# this should match what was printed earlier.
picks = mne.pick_types(raw.info, ecog=True)
dig = [raw.info['dig'][pick] for pick in picks]
chs = [raw.info['chs'][pick] for pick in picks]
pos = np.array([ch['r'] for ch in dig[:5]])
ch_names = np.array([ch['ch_name'] for ch in chs[:5]])

print("The channel montage after writing into BIDS: ")
pprint(dig[0:5])
print("The channel coordinates after writing into BIDS: ")
pprint([x for x in zip(ch_names, pos)])

# make a plot of the sensors in 2D plane
raw.plot_sensors(ch_type='ecog')


"""
.. _tut-sensor-locations:

Working with sensor locations
=============================

This tutorial describes how to read and plot sensor locations, and how
the physical location of sensors is handled in MNE-Python.

.. contents:: Page contents
   :local:
   :depth: 2

As usual we'll start by importing the modules we need and loading some
:ref:`example data <sample-dataset>`:
"""

import os
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D  # noqa
import mne

sample_data_folder = mne.datasets.sample.data_path()
sample_data_raw_file = os.path.join(sample_data_folder, 'MEG', 'sample',
                                    'sample_audvis_raw.fif')
raw = mne.io.read_raw_fif(sample_data_raw_file, preload=True, verbose=False)

###############################################################################
# About montages and layouts
# ^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# :class:`Montages <mne.channels.DigMontage>` contain sensor
# positions in 3D (``x``, ``y``, ``z``, in meters), and can be used to set
# the physical positions of sensors. By specifying the location of sensors
# relative to the brain, :class:`Montages <mne.channels.DigMontage>` play an
# important role in computing the forward solution and computing inverse
# estimates.
#
# In contrast, :class:`Layouts <mne.channels.Layout>` are *idealized* 2-D
# representations of sensor positions, and are primarily used for arranging
# individual sensor subplots in a topoplot, or for showing the *approximate*
# relative arrangement of sensors as seen from above.
#
# Working with built-in montages
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# The 3D coordinates of MEG sensors are included in the raw recordings from MEG
# systems, and are automatically stored in the ``info`` attribute of the
# :class:`~mne.io.Raw` file upon loading. EEG electrode locations are much more
# variable because of differences in head shape. Idealized montages for many
# EEG systems are included during MNE-Python installation; these files are
# stored in your ``mne-python`` directory, in the
# :file:`mne/channels/data/montages` folder:

montage_dir = os.path.join(os.path.dirname(mne.__file__),
                           'channels', 'data', 'montages')
print('\nBUILT-IN MONTAGE FILES')
print('======================')
print(sorted(os.listdir(montage_dir)))

###############################################################################
# .. sidebar:: Computing sensor locations
#
#     If you are interested in how standard ("idealized") EEG sensor positions
#     are computed on a spherical head model, the `eeg_positions`_ repository
#     provides code and documentation to this end.
#
# These built-in EEG montages can be loaded via
# :func:`mne.channels.make_standard_montage`. Note that when loading via
# :func:`~mne.channels.make_standard_montage`, provide the filename *without*
# its file extension:

ten_twenty_montage = mne.channels.make_standard_montage('standard_1020')
print(ten_twenty_montage)

###############################################################################
# Once loaded, a montage can be applied to data via one of the instance methods
# such as :meth:`raw.set_montage <mne.io.Raw.set_montage>`. It is also possible
# to skip the loading step by passing the filename string directly to the
# :meth:`~mne.io.Raw.set_montage` method. This won't work with our sample
# data, because it's channel names don't match the channel names in the
# standard 10-20 montage, so these commands are not run here:

# these will be equivalent:
# raw_1020 = raw.copy().set_montage(ten_twenty_montage)
# raw_1020 = raw.copy().set_montage('standard_1020')

###############################################################################
# :class:`Montage <mne.channels.DigMontage>` objects have a
# :meth:`~mne.channels.DigMontage.plot` method for visualization of the sensor
# locations in 3D; 2D projections are also possible by passing
# ``kind='topomap'``:

fig = ten_twenty_montage.plot(kind='3d')
fig.gca().view_init(azim=70, elev=15)
ten_twenty_montage.plot(kind='topomap', show_names=False)

###############################################################################
# .. _control-chan-projection:
#
# Controlling channel projection (MNE vs EEGLAB)
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# Channel positions in 2d space are obtained by projecting their actual 3d
# positions using a sphere as a reference. Because ``'standard_1020'`` montage
# contains realistic, not spherical, channel positions, we will use a different
# montage to demonstrate controlling how channels are projected to 2d space.

biosemi_montage = mne.channels.make_standard_montage('biosemi64')
biosemi_montage.plot(show_names=False)

###############################################################################
# By default a sphere  with an origin in ``(0, 0, 0)`` x, y, z coordinates and
# radius of ``0.095`` meters (9.5 cm) is used. You can use a different sphere
# radius by passing a single value to ``sphere`` argument in any function that
# plots channels in 2d (like :meth:`~mne.channels.DigMontage.plot` that we use
# here, but also for example :func:`mne.viz.plot_topomap`):

biosemi_montage.plot(show_names=False, sphere=0.07)

###############################################################################
# To control not only radius, but also the sphere origin, pass a
# ``(x, y, z, radius)`` tuple to ``sphere`` argument:

biosemi_montage.plot(show_names=False, sphere=(0.03, 0.02, 0.01, 0.075))

###############################################################################
# In mne-python the head center and therefore the sphere center are calculated
# using :term:`fiducial points <fiducial point>`.
# Because of this the head circle represents head
# circumference at the nasion and ear level, and not where it is commonly
# measured in 10-20 EEG system: above nasion at T4/T8, T3/T7, Oz, Fz level.
# Notice below that by default T7 and Oz channels are placed within the head
# circle, not on the head outline:

biosemi_montage.plot()

###############################################################################
# If you have previous EEGLAB experience you may prefer its convention to
# represent 10-20 head circumference with the head circle. To get EEGLAB-like
# channel layout you would have to move the sphere origin a few centimeters
# up on the z dimension:

biosemi_montage.plot(sphere=(0, 0, 0.035, 0.094))

###############################################################################
# Instead of approximating the EEGLAB-esque sphere location as above, you can
# calculate the sphere origin from position of Oz, Fpz, T3/T7 or T4/T8
# channels. This is easier once the montage has been applied to the data and
# channel positions are in the head space - see
# :ref:`this example <ex-topomap-eeglab-style>`.

###############################################################################
# .. _reading-dig-montages:
#
# Reading sensor digitization files
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# In the sample data, setting the digitized EEG montage was done prior to
# saving the :class:`~mne.io.Raw` object to disk, so the sensor positions are
# already incorporated into the ``info`` attribute of the :class:`~mne.io.Raw`
# object (see the documentation of the reading functions and
# :meth:`~mne.io.Raw.set_montage` for details on how that works). Because of
# that, we can plot sensor locations directly from the :class:`~mne.io.Raw`
# object using the :meth:`~mne.io.Raw.plot_sensors` method, which provides
# similar functionality to
# :meth:`montage.plot() <mne.channels.DigMontage.plot>`.
# :meth:`~mne.io.Raw.plot_sensors` also allows channel selection by type, can
# color-code channels in various ways (by default, channels listed in
# ``raw.info['bads']`` will be plotted in red), and allows drawing into an
# existing matplotlib ``axes`` object (so the channel positions can easily be
# made as a subplot in a multi-panel figure):

# sphinx_gallery_thumbnail_number = 8
fig = plt.figure()
ax2d = fig.add_subplot(121)
ax3d = fig.add_subplot(122, projection='3d')
raw.plot_sensors(ch_type='eeg', axes=ax2d)
raw.plot_sensors(ch_type='eeg', axes=ax3d, kind='3d')
ax3d.view_init(azim=70, elev=15)

###############################################################################
# It's probably evident from the 2D topomap above that there is some
# irregularity in the EEG sensor positions in the :ref:`sample dataset
# <sample-dataset>` — this is because the sensor positions in that dataset are
# digitizations of the sensor positions on an actual subject's head, rather
# than idealized sensor positions based on a spherical head model. Depending on
# what system was used to digitize the electrode positions (e.g., a Polhemus
# Fastrak digitizer), you must use different montage reading functions (see
# :ref:`dig-formats`). The resulting :class:`montage <mne.channels.DigMontage>`
# can then be added to :class:`~mne.io.Raw` objects by passing it to the
# :meth:`~mne.io.Raw.set_montage` method (just as we did above with the name of
# the idealized montage ``'standard_1020'``). Once loaded, locations can be
# plotted with :meth:`~mne.channels.DigMontage.plot` and saved with
# :meth:`~mne.channels.DigMontage.save`, like when working with a standard
# montage.
#
# .. note::
#
#     When setting a montage with :meth:`~mne.io.Raw.set_montage`
#     the measurement info is updated in two places (the ``chs``
#     and ``dig`` entries are updated). See :ref:`tut-info-class`.
#     ``dig`` may contain HPI, fiducial, or head shape points in
#     addition to electrode locations.
#
#
# Rendering sensor position with mayavi
# ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#
# It is also possible to render an image of a MEG sensor helmet in 3D, using
# mayavi instead of matplotlib, by calling :func:`mne.viz.plot_alignment`

fig = mne.viz.plot_alignment(raw.info, trans=None, dig=False, eeg=False,
                             surfaces=[], meg=['helmet', 'sensors'],
                             coord_frame='meg')
mne.viz.set_3d_view(fig, azimuth=50, elevation=90, distance=0.5)

###############################################################################
# :func:`~mne.viz.plot_alignment` requires an :class:`~mne.Info` object, and
# can also render MRI surfaces of the scalp, skull, and brain (by passing
# keywords like ``'head'``, ``'outer_skull'``, or ``'brain'`` to the
# ``surfaces`` parameter) making it useful for :ref:`assessing coordinate frame
# transformations <plot_source_alignment>`. For examples of various uses of
# :func:`~mne.viz.plot_alignment`, see :ref:`plot_montage`,
# :doc:`../../auto_examples/visualization/plot_eeg_on_scalp`, and
# :doc:`../../auto_examples/visualization/plot_meg_sensors`.
#
#
# Working with layout files
# ^^^^^^^^^^^^^^^^^^^^^^^^^
#
# As with montages, many layout files are included during MNE-Python
# installation, and are stored in the :file:`mne/channels/data/layouts` folder:

layout_dir = os.path.join(os.path.dirname(mne.__file__),
                          'channels', 'data', 'layouts')
print('\nBUILT-IN LAYOUT FILES')
print('=====================')
print(sorted(os.listdir(layout_dir)))

###############################################################################
# You may have noticed that the file formats and filename extensions of the
# built-in layout and montage files vary considerably. This reflects different
# manufacturers' conventions; to make loading easier the montage and layout
# loading functions in MNE-Python take the filename *without its extension* so
# you don't have to keep track of which file format is used by which
# manufacturer.
#
# To load a layout file, use the :func:`mne.channels.read_layout` function, and
# provide the filename *without* its file extension. You can then visualize the
# layout using its :meth:`~mne.channels.Layout.plot` method, or (equivalently)
# by passing it to :func:`mne.viz.plot_layout`:

biosemi_layout = mne.channels.read_layout('biosemi')
biosemi_layout.plot()  # same result as: mne.viz.plot_layout(biosemi_layout)

###############################################################################
# Similar to the ``picks`` argument for selecting channels from
# :class:`~mne.io.Raw` objects, the :meth:`~mne.channels.Layout.plot` method of
# :class:`~mne.channels.Layout` objects also has a ``picks`` argument. However,
# because layouts only contain information about sensor name and location (not
# sensor type), the :meth:`~mne.channels.Layout.plot` method only allows
# picking channels by index (not by name or by type). Here we find the indices
# we want using :func:`numpy.where`; selection by name or type is possible via
# :func:`mne.pick_channels` or :func:`mne.pick_types`.

midline = np.where([name.endswith('z') for name in biosemi_layout.names])[0]
biosemi_layout.plot(picks=midline)

###############################################################################
# If you're working with a :class:`~mne.io.Raw` object that already has sensor
# positions incorporated, you can create a :class:`~mne.channels.Layout` object
# with either the :func:`mne.channels.make_eeg_layout` function or
# (equivalently) the :func:`mne.channels.find_layout` function.

layout_from_raw = mne.channels.make_eeg_layout(raw.info)
# same result as: mne.channels.find_layout(raw.info, ch_type='eeg')
layout_from_raw.plot()

###############################################################################
# .. note::
#
#     There is no corresponding ``make_meg_layout`` function because sensor
#     locations are fixed in a MEG system (unlike in EEG, where the sensor caps
#     deform to fit each subject's head). Thus MEG layouts are consistent for a
#     given system and you can simply load them with
#     :func:`mne.channels.read_layout`, or use :func:`mne.channels.find_layout`
#     with the ``ch_type`` parameter, as shown above for EEG.
#
# All :class:`~mne.channels.Layout` objects have a
# :meth:`~mne.channels.Layout.save` method that allows writing layouts to disk,
# in either :file:`.lout` or :file:`.lay` format (which format gets written is
# inferred from the file extension you pass to the method's ``fname``
# parameter). The choice between :file:`.lout` and :file:`.lay` format only
# matters if you need to load the layout file in some other software
# (MNE-Python can read either format equally well).
#
#
# .. LINKS
#
# .. _`eeg_positions`: https://github.com/sappelhoff/eeg_positions


"""
Find the longest palindrome in the following 1169-character string:

Fourscoreandsevenyearsagoourfaathersbroughtforthonthisconta
inentanewnationconceivedinzLibertyanddedicatedtotheproposit
ionthatallmenarecreatedequalNowweareengagedinagreahtcivilwa
rtestingwhetherthatnaptionoranynartionsoconceivedandsodedic
atedcanlongendureWeareqmetonagreatbattlefiemldoftzhatwarWeh
avecometodedicpateaportionofthatfieldasafinalrestingplacefo
rthosewhoheregavetheirlivesthatthatnationmightliveItisaltog
etherfangandproperthatweshoulddothisButinalargersensewecann
otdedicatewecannotconsecratewecannothallowthisgroundThebrav
elmenlivinganddeadwhostruggledherehaveconsecrateditfarabove
ourpoorponwertoaddordetractTgheworldadswfilllittlenotlenorl
ongrememberwhatwesayherebutitcanneverforgetwhattheydidhereI
tisforusthelivingrathertobededicatedheretotheulnfinishedwor
kwhichtheywhofoughtherehavethusfarsonoblyadvancedItisrather
forustobeherededicatedtothegreattdafskremainingbeforeusthat
fromthesehonoreddeadwetakeincreaseddevotiontothatcauseforwh
ichtheygavethelastpfullmeasureofdevotionthatweherehighlyres
olvethatthesedeadshallnothavediedinvainthatthisnationunsder
Godshallhaveanewbirthoffreedomandthatgovernmentofthepeopleb
ythepeopleforthepeopleshallnotperishfromtheearth

Your task is to write a function that finds the longest palindrome in a string and apply it to the string given above.

    taken from http://challenge.greplin.com/ :)

It seems the number of users giving challenges have been reduced. Since my final exams are going on and its kinda
difficult to think of all the challenges, I kindly request you all to suggest us interesting challenges at
/r/dailyprogrammer_ideas .. Thank you!
"""

import numpy as np

def longest_common_substring(str1, str2):
    """ from 20120602C """
    l1 = len(str1)
    l2 = len(str2)

    if l2 < l1:
        l1, l2 = l2, l1
        str1, str2 = str2, str1

    L = np.zeros((l1, l2))
    z = 0
    ret = []
    """ based on pseudocode from wikipedia """
    for i in range(l1):
        for j in range(l2):
            if str1[i] == str2[j]:
                if i == 0 or j == 0:
                    L[i, j] = 1
                else:
                    L[i, j] = L[i-1, j-1] + 1
                if L[i, j] > z:
                    z = int(L[i, j])
                    ret = [str1[i-z+1:i+1]]
                elif L[i, j] == z:
                    ret.append(str1[i-z+1:i+1])
            else:
                L[i, j] = 0

    if len(ret) == 1:
        return ret[0]
    else:
        return ret

def enforce_palindrom(answer):
    if type(answer) == list:
        answer = list(map(lambda x: x == x[::-1], answer))
    return answer

def main():
    string = 'Fourscoreandsevenyearsagoourfaathersbroughtforthonthisconta' \
             'inentanewnationconceivedinzLibertyanddedicatedtotheproposit' \
             'ionthatallmenarecreatedequalNowweareengagedinagreahtcivilwa' \
             'rtestingwhetherthatnaptionoranynartionsoconceivedandsodedic' \
             'atedcanlongendureWeareqmetonagreatbattlefiemldoftzhatwarWeh' \
             'avecometodedicpateaportionofthatfieldasafinalrestingplacefo' \
             'rthosewhoheregavetheirlivesthatthatnationmightliveItisaltog' \
             'etherfangandproperthatweshoulddothisButinalargersensewecann' \
             'otdedicatewecannotconsecratewecannothallowthisgroundThebrav' \
             'elmenlivinganddeadwhostruggledherehaveconsecrateditfarabove' \
             'ourpoorponwertoaddordetractTgheworldadswfilllittlenotlenorl' \
             'ongrememberwhatwesayherebutitcanneverforgetwhattheydidhereI' \
             'tisforusthelivingrathertobededicatedheretotheulnfinishedwor' \
             'kwhichtheywhofoughtherehavethusfarsonoblyadvancedItisrather' \
             'forustobeherededicatedtothegreattdafskremainingbeforeusthat' \
             'fromthesehonoreddeadwetakeincreaseddevotiontothatcauseforwh' \
             'ichtheygavethelastpfullmeasureofdevotionthatweherehighlyres' \
             'olvethatthesedeadshallnothavediedinvainthatthisnationunsder' \
             'Godshallhaveanewbirthoffreedomandthatgovernmentofthepeopleb' \
             'ythepeopleforthepeopleshallnotperishfromtheearth'

    string = string.lower()

    answer = longest_common_substring(string, string[::-1])
    print(answer)

if __name__ == "__main__":
    main()


"""
Handlers for IPythonDirective's @doctest pseudo-decorator.

The Sphinx extension that provides support for embedded yap_ipython code provides
a pseudo-decorator @doctest, which treats the input/output block as a
doctest, raising a RuntimeError during doc generation if the actual output
(after running the input) does not match the expected output.

An example usage is:

.. code-block:: rst

   .. ipython::

        In [1]: x = 1

        @doctest
        In [2]: x + 2
        Out[3]: 3

One can also provide arguments to the decorator. The first argument should be
the name of a custom handler. The specification of any other arguments is
determined by the handler. For example,

.. code-block:: rst

      .. ipython::

         @doctest float
         In [154]: 0.1 + 0.2
         Out[154]: 0.3

allows the actual output ``0.30000000000000004`` to match the expected output
due to a comparison with `np.allclose`.

This module contains handlers for the @doctest pseudo-decorator. Handlers
should have the following function signature::

    handler(sphinx_shell, args, input_lines, found, submitted)

where `sphinx_shell` is the embedded Sphinx shell, `args` contains the list
of arguments that follow: '@doctest handler_name', `input_lines` contains
a list of the lines relevant to the current doctest, `found` is a string
containing the output from the yap_ipython shell, and `submitted` is a string
containing the expected output from the yap_ipython shell.

Handlers must be registered in the `doctests` dict at the end of this module.

"""

def str_to_array(s):
    """
    Simplistic converter of strings from repr to float NumPy arrays.

    If the repr representation has ellipsis in it, then this will fail.

    Parameters
    ----------
    s : str
        The repr version of a NumPy array.

    Examples
    --------
    >>> s = "array([ 0.3,  inf,  nan])"
    >>> a = str_to_array(s)

    """
    import numpy as np

    # Need to make sure eval() knows about inf and nan.
    # This also assumes default printoptions for NumPy.
    from numpy import inf, nan

    if s.startswith(u'array'):
        # Remove array( and )
        s = s[6:-1]

    if s.startswith(u'['):
        a = np.array(eval(s), dtype=float)
    else:
        # Assume its a regular float. Force 1D so we can index into it.
        a = np.atleast_1d(float(s))
    return a

def float_doctest(sphinx_shell, args, input_lines, found, submitted):
    """
    Doctest which allow the submitted output to vary slightly from the input.

    Here is how it might appear in an rst file:

    .. code-block:: rst

       .. ipython::

          @doctest float
          In [1]: 0.1 + 0.2
          Out[1]: 0.3

    """
    import numpy as np

    if len(args) == 2:
        rtol = 1e-05
        atol = 1e-08
    else:
        # Both must be specified if any are specified.
        try:
            rtol = float(args[2])
            atol = float(args[3])
        except IndexError:
            e = ("Both `rtol` and `atol` must be specified "
                 "if either are specified: {0}".format(args))
            raise IndexError(e)

    try:
        submitted = str_to_array(submitted)
        found = str_to_array(found)
    except:
        # For example, if the array is huge and there are ellipsis in it.
        error = True
    else:
        found_isnan = np.isnan(found)
        submitted_isnan = np.isnan(submitted)
        error = not np.allclose(found_isnan, submitted_isnan)
        error |= not np.allclose(found[~found_isnan],
                                 submitted[~submitted_isnan],
                                 rtol=rtol, atol=atol)

    TAB = ' ' * 4
    directive = sphinx_shell.directive
    if directive is None:
        source = 'Unavailable'
        content = 'Unavailable'
    else:
        source = directive.state.document.current_source
        # Add tabs and make into a single string.
        content = '\n'.join([TAB + line for line in directive.content])

    if error:

        e = ('doctest float comparison failure\n\n'
             'Document source: {0}\n\n'
             'Raw content: \n{1}\n\n'
             'On input line(s):\n{TAB}{2}\n\n'
             'we found output:\n{TAB}{3}\n\n'
             'instead of the expected:\n{TAB}{4}\n\n')
        e = e.format(source, content, '\n'.join(input_lines), repr(found),
                     repr(submitted), TAB=TAB)
        raise RuntimeError(e)

# dict of allowable doctest handlers. The key represents the first argument
# that must be given to @doctest in order to activate the handler.
doctests = {
    'float': float_doctest,
}