# Path: sdopt/parsers/ampl_parser.py
# def read_flattened_ampl(filename, show_sparsity_pattern=True):
#     bsp = read_nl(filename)
#     bsp.csr_mat = sp.csr_matrix((bsp.csr_data, bsp.csr_indices, bsp.csr_indptr), 
#                                 shape=(bsp.nrows,bsp.ncols) ) 
#     check_J_segment(bsp)
#     dbg_info(bsp)
#     set_permutation_with_block_boundaries(bsp)
#     bsp.row_names = read_names(filename, 'row', bsp.nrows)
#     bsp.col_names = read_names(filename, 'col', bsp.ncols)
#     plot_sparsity(bsp, show_sparsity_pattern)
#     return bsp
# 
# Path: sdopt/representation/problem.py
# class Problem:
#     
#     @staticmethod
#     def createFrom(dagfilename, crosscheck_nl=True, show_sparsity=True):
#         from ..parsers.dag_parser import read_problem
#         return read_problem(dagfilename, crosscheck_nl, show_sparsity)
# 
#     def __init__(self):
#         self.dag = nx.DiGraph()
#         self.con_ends_num = { } # con sink node -> con num (in AMPL)
#         self.con_num_name = { } # con num -> con name (in AMPL)
#         self.var_num_name = { } # var num (in AMPL) -> var name (in AMPL)
#         self.var_node_ids = set() # initially all var node ids
#         self.var_num_id   = { }   # only base vars: var num -> smallest node id
#         self.base_vars   = { }    # base var node id -> var num
#         self.defined_vars = set() # node ids after elimination
#         self.model_name   = '(none)'
#         self.nvars        = int(-1) # number of variables
#         self.con_top_ord  = { } # con sink node -> con topological order
#         self.ncons  = None
#         self.nzeros = None        
#         self.refsols      = [ ]
# 
# def setup(prob):
#     du.dbg_info(prob.dag)
#     prob.dag.graph['name'] = prob.model_name
#     setup_constraint_names(prob)
#     setup_nodes(prob)
#     # for base vars: setup_nodes() only stores the smallest id in var_num_id
#     prob.base_vars = { v : k for k, v in six.iteritems(prob.var_num_id) }
#     # The followings are simplifications
#     #-------------------------------------------
#     # remove bogus base var aliasing
#     prob.var_node_ids.difference_update(six.iterkeys(prob.base_vars))
#     # the rest assumes that base var nodes are no longer in var_node_ids!
#     remove_var_aliases(prob)
#     #-------------------------------------------
#     # nl2dag erroneously turns defined vars into constraints
#     reconstruct_CSEs(prob)
#     remove_unused_def_vars(prob)
#     remove_identity_sum_nodes(prob)
#     remove_def_var_aliasing_another_node(prob)
#     #-------------------------------------------
#     collect_constraint_topological_orders(prob)
#     #-------------------------------------------
#     # TODO pretty print constraints
#     #-------------------------------------------
#     du.dbg_info(prob.dag, lambda : dbg_problem_statistics(prob))
# 
# def crosscheck_sparsity_pattern(prob, jacobian):
#     check_shape(prob, jacobian)
#     checked = [False] * jacobian.shape[0]
#     for con_num, n in du.itr_sink_con_num_nodeid(prob.dag):
#         base_vars = np.array(base_var_nums_in_con(prob, n), np.int32)
#         cols = cols_in_row(jacobian, con_num)
#         assert np.all(cols==base_vars)
#         checked[con_num] = True
#     assert all(checked)
# 
# def crosscheck_names(prob, row_names, col_names):
#     assert set(row_names) <= set(six.itervalues(prob.con_num_name))
#     assert set(col_names) <= set(six.itervalues(prob.var_num_name))
#     assert len(row_names) == prob.ncons
#     assert len(col_names) == prob.nvars
# 
# Path: sdopt/util/file_reader.py
# @contextmanager
# def lines_of(filename):
#     print('Trying to read file \"%s\"' % filename)
#     try:
#         f = fileinput.input(filename, mode='r')
#         yield _rstripped_lines(f)
#     finally:
#         print('Read ',f.lineno(),' lines from file \"',filename,'\"', sep='')
#         f.close()


from . import edge_line
from . import hint_line
from . import info_line
from . import node_line
from .ampl_parser import read_flattened_ampl
from ..representation.problem import Problem, setup, \
                                crosscheck_sparsity_pattern, crosscheck_names
from ..util.file_reader import lines_of
# FIXME show_sparsity=False
def read_problem(filename, crosscheck_nl=True, show_sparsity=True):
    with lines_of(filename) as lines:
        problem = parse(lines)
    setup(problem)
    if crosscheck_nl:
        nl_filename = filename[:-4]+'.nl' # filename.dag -> filename.nl
        bsp = read_flattened_ampl(nl_filename, show_sparsity)
        crosscheck_sparsity_pattern(problem, bsp.csr_mat)
        crosscheck_names(problem, bsp.row_names, bsp.col_names)
    return problem

def lines(iterable):
    for line in iterable:
        first_space = line.find(' ')
        kind  = line[1:first_space-1]
        elems = [e.split() for e in line[first_space:].split(':')]
        if kind.isdigit():
            elems.append(int(kind))
            kind = 'I'
        yield kind, elems

def parse(f):
    funcs = { 'N': info_line.parse,
              'I': node_line.parse,
              'E': edge_line.parse,
              'H': hint_line.parse }
    p = Problem()
    for kind, elems in lines(f):
        func = funcs.get(kind)

# Path: sdopt/parsers/ampl_parser.py
# def read_flattened_ampl(filename, show_sparsity_pattern=True):
#     bsp = read_nl(filename)
#     bsp.csr_mat = sp.csr_matrix((bsp.csr_data, bsp.csr_indices, bsp.csr_indptr), 
#                                 shape=(bsp.nrows,bsp.ncols) ) 
#     check_J_segment(bsp)
#     dbg_info(bsp)
#     set_permutation_with_block_boundaries(bsp)
#     bsp.row_names = read_names(filename, 'row', bsp.nrows)
#     bsp.col_names = read_names(filename, 'col', bsp.ncols)
#     plot_sparsity(bsp, show_sparsity_pattern)
#     return bsp
# 
# Path: sdopt/representation/problem.py
# class Problem:
#     
#     @staticmethod
#     def createFrom(dagfilename, crosscheck_nl=True, show_sparsity=True):
#         from ..parsers.dag_parser import read_problem
#         return read_problem(dagfilename, crosscheck_nl, show_sparsity)
# 
#     def __init__(self):
#         self.dag = nx.DiGraph()
#         self.con_ends_num = { } # con sink node -> con num (in AMPL)
#         self.con_num_name = { } # con num -> con name (in AMPL)
#         self.var_num_name = { } # var num (in AMPL) -> var name (in AMPL)
#         self.var_node_ids = set() # initially all var node ids
#         self.var_num_id   = { }   # only base vars: var num -> smallest node id
#         self.base_vars   = { }    # base var node id -> var num
#         self.defined_vars = set() # node ids after elimination
#         self.model_name   = '(none)'
#         self.nvars        = int(-1) # number of variables
#         self.con_top_ord  = { } # con sink node -> con topological order
#         self.ncons  = None
#         self.nzeros = None        
#         self.refsols      = [ ]
# 
# def setup(prob):
#     du.dbg_info(prob.dag)
#     prob.dag.graph['name'] = prob.model_name
#     setup_constraint_names(prob)
#     setup_nodes(prob)
#     # for base vars: setup_nodes() only stores the smallest id in var_num_id
#     prob.base_vars = { v : k for k, v in six.iteritems(prob.var_num_id) }
#     # The followings are simplifications
#     #-------------------------------------------
#     # remove bogus base var aliasing
#     prob.var_node_ids.difference_update(six.iterkeys(prob.base_vars))
#     # the rest assumes that base var nodes are no longer in var_node_ids!
#     remove_var_aliases(prob)
#     #-------------------------------------------
#     # nl2dag erroneously turns defined vars into constraints
#     reconstruct_CSEs(prob)
#     remove_unused_def_vars(prob)
#     remove_identity_sum_nodes(prob)
#     remove_def_var_aliasing_another_node(prob)
#     #-------------------------------------------
#     collect_constraint_topological_orders(prob)
#     #-------------------------------------------
#     # TODO pretty print constraints
#     #-------------------------------------------
#     du.dbg_info(prob.dag, lambda : dbg_problem_statistics(prob))
# 
# def crosscheck_sparsity_pattern(prob, jacobian):
#     check_shape(prob, jacobian)
#     checked = [False] * jacobian.shape[0]
#     for con_num, n in du.itr_sink_con_num_nodeid(prob.dag):
#         base_vars = np.array(base_var_nums_in_con(prob, n), np.int32)
#         cols = cols_in_row(jacobian, con_num)
#         assert np.all(cols==base_vars)
#         checked[con_num] = True
#     assert all(checked)
# 
# def crosscheck_names(prob, row_names, col_names):
#     assert set(row_names) <= set(six.itervalues(prob.con_num_name))
#     assert set(col_names) <= set(six.itervalues(prob.var_num_name))
#     assert len(row_names) == prob.ncons
#     assert len(col_names) == prob.nvars
# 
# Path: sdopt/util/file_reader.py
# @contextmanager
# def lines_of(filename):
#     print('Trying to read file \"%s\"' % filename)
#     try:
#         f = fileinput.input(filename, mode='r')
#         yield _rstripped_lines(f)
#     finally:
#         print('Read ',f.lineno(),' lines from file \"',filename,'\"', sep='')
#         f.close()


# Path: sdopt/parsers/dag_parser.py
from . import edge_line
from . import hint_line
from . import info_line
from . import node_line
from .ampl_parser import read_flattened_ampl
from ..representation.problem import Problem, setup, \
                                crosscheck_sparsity_pattern, crosscheck_names
from ..util.file_reader import lines_of

# FIXME show_sparsity=False
def read_problem(filename, crosscheck_nl=True, show_sparsity=True):
    with lines_of(filename) as lines:
        problem = parse(lines)
    setup(problem)
    if crosscheck_nl:
        nl_filename = filename[:-4]+'.nl' # filename.dag -> filename.nl
        bsp = read_flattened_ampl(nl_filename, show_sparsity)
        crosscheck_sparsity_pattern(problem, bsp.csr_mat)
        crosscheck_names(problem, bsp.row_names, bsp.col_names)
    return problem

def lines(iterable):
    for line in iterable:
        first_space = line.find(' ')
        kind  = line[1:first_space-1]
        elems = [e.split() for e in line[first_space:].split(':')]
        if kind.isdigit():
            elems.append(int(kind))
            kind = 'I'
        yield kind, elems

def parse(f):
    funcs = { 'N': info_line.parse,
              'I': node_line.parse,
              'E': edge_line.parse,
              'H': hint_line.parse }
    p = Problem()
    for kind, elems in lines(f):
        func = funcs.get(kind)

        if func: