# Path: retina/screen/map/mapimpl.py
# class AlbersProjectionMap(PointMap):
#     """ https://en.wikipedia.org/wiki/Albers_projection
#         Compared to the entry of 8/21/2015
#         (numbers were chosen to have simpler formulas)
#         phi1 = 90, phi2 = 0, phi0 = 90, lambda0 = 90
#         lambda <- 2n (lambda - lambda0) (doubles the area)
#         =>
#         n = 0.5
#         rho0 = 0, C = 1, rho = 2sqrt(1-sin(phi))
#     """
# 
#     def __init__(self, r, eulerangles=None):
#         """ r: radious of the sphere from which the projection is made
#         """
#         self._r = r
#         self.rotationmap = EulerAnglesMap(eulerangles)
#         self.x_rotationmap = EulerAnglesMap(eulerangles=[0, np.pi/2, 0])
# 
#     def map(self, elev, azim):
#         """ Returns (nan, nan) if point cannot be mapped else (x, y)
#             arguments can be numpy arrays or scalars
#         """
#         elev, azim = self.rotationmap.invmap(elev, azim)
#         elev, azim = self.x_rotationmap.invmap(elev, azim)
# 
#         rxy = self._r*np.sqrt(1-np.sin(elev))
#         x = -rxy*np.cos(azim)
#         y = -rxy*np.sin(azim)
#         return (x, y)
# 
#     def invmap(self, x, y):
#         """ Returns (nan, nan) if point cannot be mapped else
#             (elevation, azimuth)
#             arguments can be numpy arrays or scalars
#         """
#         r = self._r
#         rxy = np.sqrt(x*x + y*y)
# 
#         elev = np.arcsin(1-(rxy/r)**2)
#         azim = np.arctan2(-y, -x)
# 
#         # this angle is nan if rxy = 0 but can be anything
#         try:
#             azim[np.isnan(azim)] = 0
#         except TypeError:  # Thrown if azim is scalar
#             if np.isnan(azim):
#                 azim = 0
# 
#         try:
#             azim[np.isnan(elev)] = np.nan
#         except TypeError:  # Thrown if azim is scalar
#             if np.isnan(elev):
#                 azim = np.nan
# 
#         elev, azim = self.x_rotationmap.map(elev, azim)
#         return self.rotationmap.map(elev, azim)
# 
# Path: retina/configreader.py
# class ConfigReader(object):
#     '''
#         This class loads and validates configuration
#         given configuration and specification files.
#         It is reserved for other custom actions but
#         for now configuration can be accessed as
#         `reader_obj.conf` once it is successfully loaded
#     '''
#     def __init__(self, conf_file, conf_specfile):
# 
#         if conf_file is None:
#             self._conf = ConfigObj()
#         else:
#             config = ConfigObj(conf_file, configspec=conf_specfile,
#                                file_error=True)
#             validator = Validator()
#             res = config.validate(validator, preserve_errors=True)
# 
#             # res is not boolean necessarily
#             if res is True:
#                 self._conf = config
#             else:
#                 self._conf = None
#                 self.print_validation_errors(config, res)
#                 raise ValueError('Failed to validate file {} using'
#                                  ' specification {}'.format(conf_file,
#                                                             conf_specfile))
# 
#     @classmethod
#     def print_validation_errors(cls, config, res):
#         for entry in flatten_errors(config, res):
#             # each entry is a tuple
#             section_list, key, error = entry
#             if key is not None:
#                 section_list.append(key)
#             else:
#                 section_list.append('[missing section]')
#             section_string = ', '.join(section_list)
#             if error is False:
#                 error = 'Missing value or section.'
#             print(section_string, ' = ', error)
# 
#     @property
#     def conf(self):
#         return self._conf


import os, resource, sys
import argparse
import networkx as nx
import numpy as np
import neurokernel.core_gpu as core
import retina.retina as ret
import retina.geometry.hexagon as hx
import gen_input as gi
    import neurokernel.mpi_relaunch
from neurokernel.pattern import Pattern
from neurokernel.tools.logging import setup_logger
from neurokernel.tools.timing import Timer
from retina.input_generator import RetinaInputGenerator
from retina.screen.map.mapimpl import AlbersProjectionMap
from retina.configreader import ConfigReader
from retina.LPU import LPU


def get_worker_id(i):
    return 'retina{}'.format(i)


# number of neurons of `j`th worker out of `worker_num`
# with `total_neurons` neurons overall
def get_worker_num_neurons(j, total_neurons, worker_num):
    num_neurons = (total_neurons-1) // worker_num + 1
    return min(num_neurons, total_neurons - j*num_neurons)


def add_master_LPU(config, i, retina, manager, generator):
    dt = config['General']['dt']
    debug = config['Retina']['debug']
    time_sync = config['Retina']['time_sync']

    input_filename = config['Retina']['input_file']
    output_filename = config['Retina']['output_file']
    gexf_filename = config['Retina']['gexf_file']
    suffix = config['General']['file_suffix']

    if generator is None:
        input_file = '{}{}{}.h5'.format(input_filename, i, suffix)
    else:
        input_file = None
    output_file = '{}{}{}.h5'.format(output_filename, i, suffix)
    gexf_file = '{}{}{}.gexf.gz'.format(gexf_filename, i, suffix)


# Path: retina/screen/map/mapimpl.py
# class AlbersProjectionMap(PointMap):
#     """ https://en.wikipedia.org/wiki/Albers_projection
#         Compared to the entry of 8/21/2015
#         (numbers were chosen to have simpler formulas)
#         phi1 = 90, phi2 = 0, phi0 = 90, lambda0 = 90
#         lambda <- 2n (lambda - lambda0) (doubles the area)
#         =>
#         n = 0.5
#         rho0 = 0, C = 1, rho = 2sqrt(1-sin(phi))
#     """
# 
#     def __init__(self, r, eulerangles=None):
#         """ r: radious of the sphere from which the projection is made
#         """
#         self._r = r
#         self.rotationmap = EulerAnglesMap(eulerangles)
#         self.x_rotationmap = EulerAnglesMap(eulerangles=[0, np.pi/2, 0])
# 
#     def map(self, elev, azim):
#         """ Returns (nan, nan) if point cannot be mapped else (x, y)
#             arguments can be numpy arrays or scalars
#         """
#         elev, azim = self.rotationmap.invmap(elev, azim)
#         elev, azim = self.x_rotationmap.invmap(elev, azim)
# 
#         rxy = self._r*np.sqrt(1-np.sin(elev))
#         x = -rxy*np.cos(azim)
#         y = -rxy*np.sin(azim)
#         return (x, y)
# 
#     def invmap(self, x, y):
#         """ Returns (nan, nan) if point cannot be mapped else
#             (elevation, azimuth)
#             arguments can be numpy arrays or scalars
#         """
#         r = self._r
#         rxy = np.sqrt(x*x + y*y)
# 
#         elev = np.arcsin(1-(rxy/r)**2)
#         azim = np.arctan2(-y, -x)
# 
#         # this angle is nan if rxy = 0 but can be anything
#         try:
#             azim[np.isnan(azim)] = 0
#         except TypeError:  # Thrown if azim is scalar
#             if np.isnan(azim):
#                 azim = 0
# 
#         try:
#             azim[np.isnan(elev)] = np.nan
#         except TypeError:  # Thrown if azim is scalar
#             if np.isnan(elev):
#                 azim = np.nan
# 
#         elev, azim = self.x_rotationmap.map(elev, azim)
#         return self.rotationmap.map(elev, azim)
# 
# Path: retina/configreader.py
# class ConfigReader(object):
#     '''
#         This class loads and validates configuration
#         given configuration and specification files.
#         It is reserved for other custom actions but
#         for now configuration can be accessed as
#         `reader_obj.conf` once it is successfully loaded
#     '''
#     def __init__(self, conf_file, conf_specfile):
# 
#         if conf_file is None:
#             self._conf = ConfigObj()
#         else:
#             config = ConfigObj(conf_file, configspec=conf_specfile,
#                                file_error=True)
#             validator = Validator()
#             res = config.validate(validator, preserve_errors=True)
# 
#             # res is not boolean necessarily
#             if res is True:
#                 self._conf = config
#             else:
#                 self._conf = None
#                 self.print_validation_errors(config, res)
#                 raise ValueError('Failed to validate file {} using'
#                                  ' specification {}'.format(conf_file,
#                                                             conf_specfile))
# 
#     @classmethod
#     def print_validation_errors(cls, config, res):
#         for entry in flatten_errors(config, res):
#             # each entry is a tuple
#             section_list, key, error = entry
#             if key is not None:
#                 section_list.append(key)
#             else:
#                 section_list.append('[missing section]')
#             section_string = ', '.join(section_list)
#             if error is False:
#                 error = 'Missing value or section.'
#             print(section_string, ' = ', error)
# 
#     @property
#     def conf(self):
#         return self._conf


# Path: examples/retina_demo/retina_demo.py
import os, resource, sys
import argparse
import networkx as nx
import numpy as np
import neurokernel.core_gpu as core
import retina.retina as ret
import retina.geometry.hexagon as hx
import gen_input as gi
    import neurokernel.mpi_relaunch
from neurokernel.pattern import Pattern
from neurokernel.tools.logging import setup_logger
from neurokernel.tools.timing import Timer
from retina.input_generator import RetinaInputGenerator
from retina.screen.map.mapimpl import AlbersProjectionMap
from retina.configreader import ConfigReader
from retina.LPU import LPU



def get_worker_id(i):
    return 'retina{}'.format(i)


# number of neurons of `j`th worker out of `worker_num`
# with `total_neurons` neurons overall
def get_worker_num_neurons(j, total_neurons, worker_num):
    num_neurons = (total_neurons-1) // worker_num + 1
    return min(num_neurons, total_neurons - j*num_neurons)


def add_master_LPU(config, i, retina, manager, generator):
    dt = config['General']['dt']
    debug = config['Retina']['debug']
    time_sync = config['Retina']['time_sync']

    input_filename = config['Retina']['input_file']
    output_filename = config['Retina']['output_file']
    gexf_filename = config['Retina']['gexf_file']
    suffix = config['General']['file_suffix']

    if generator is None:
        input_file = '{}{}{}.h5'.format(input_filename, i, suffix)
    else:
        input_file = None
    output_file = '{}{}{}.h5'.format(output_filename, i, suffix)
    gexf_file = '{}{}{}.gexf.gz'.format(gexf_filename, i, suffix)


    G = retina.get_master_graph()