# Path: pathpy/Log.py
# class Log:
#     """ A simple logging class, that allows to select what messages should
#         be recorded in the output, and where these message should be directed.
#     """
# 
#     ## the output stream to which log entries will be written
#     output_stream = sys.stdout
# 
#     ## The minimum severity level of messages to be logged
#     min_severity  = Severity.INFO
# 
# 
#     @staticmethod
#     def setMinSeverity(severity):
#         """ Sets the minimum sveerity level a message
#         needs to have in order to be recorded in the output stream.
#         By default, any message which has a severity of at least
#         Severity.INFO will be written to the output stream. All messages
#         with lower priority will be surpressed.
#         """
#         Log.min_severity = severity
# 
# 
#     @staticmethod
#     def setOutputStream(stream):
#         """ Sets the output stream to which all messages will be
#             written. By default, this is sys.stdout, but it can be
#             changed in order to redirect the log to a logfile.
#         """
#         output_stream = stream
# 
# 
#     @staticmethod
#     def add(msg, severity=Severity.INFO):
#         """ Adds a message with the given severity to the log. This message will be written
#             to the log output stream, which by default is sys.stdout. A newline character
#             will be added to the message by default.
#         """
#         if severity >= Log.min_severity:
#             ts = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
#             Log.output_stream.write(ts + ' [' + str(severity) + ']\t' + msg + '\n')
#             Log.output_stream.flush()
# 
# Path: pathpy/Log.py
# class Severity(enum.IntEnum):
#     """ An enumeration that can be used to indicate
#         the severity of log messages, and which can be
#         used tpo filter messages based on severities.
#     """
# 
#     ## Error messages
#     ERROR = 4
# 
#     ## Warning messages
#     WARNING = 3
# 
#     ## Informational messages (default minimum level)
#     INFO = 2
# 
#     ## Messages regarding timing and performance
#     TIMING = 1
# 
#     ## Debug messages (really verbose)
#     DEBUG = 0


import collections as _co
import bisect as _bs
import itertools as _iter
import numpy as _np 
import scipy.sparse as _sparse
import scipy.sparse.linalg as _sla
import scipy.linalg as _la
import scipy as _sp
from pathpy.Log import Log
from pathpy.Log import Severity
    def getDoF(self, assumption="paths"):
        """
        Calculates the degrees of freedom (i.e. number of parameters) of 
        this k-order model. Depending on the modeling assumptions, this either
        corresponds to the number of paths of length k in the first-order network 
        or to the number of all possible k-grams. The degrees of freedom of a model 
        can be used to assess the model complexity when calculating, e.g., the 
        Bayesian Information Criterion (BIC).

        @param assumption: if set to 'paths', for the degree of freedon calculation in the BIC, 
            only paths in the first-order network topology will be considered. This is 
            needed whenever we are interested in a modeling of paths in a given network topology.
            If set to 'ngrams' all possible n-grams will be considered, independent of whether they 
            are valid paths in the first-order network or not. The 'ngrams' and the 'paths' assumption 
            coincide if the first-order network is fully connected.
        """
        assert assumption == 'paths' or assumption == 'ngrams', 'Error: Invalid assumption'
        
        if assumption == 'paths':            
            return self.dof_paths
        else:            
            return self.dof_ngrams   


    def getDistanceMatrix(self):
        """
        Calculates shortest path distances between all pairs of 
        higher-order nodes using the Floyd-Warshall algorithm.
        """


# Path: pathpy/Log.py
# class Log:
#     """ A simple logging class, that allows to select what messages should
#         be recorded in the output, and where these message should be directed.
#     """
# 
#     ## the output stream to which log entries will be written
#     output_stream = sys.stdout
# 
#     ## The minimum severity level of messages to be logged
#     min_severity  = Severity.INFO
# 
# 
#     @staticmethod
#     def setMinSeverity(severity):
#         """ Sets the minimum sveerity level a message
#         needs to have in order to be recorded in the output stream.
#         By default, any message which has a severity of at least
#         Severity.INFO will be written to the output stream. All messages
#         with lower priority will be surpressed.
#         """
#         Log.min_severity = severity
# 
# 
#     @staticmethod
#     def setOutputStream(stream):
#         """ Sets the output stream to which all messages will be
#             written. By default, this is sys.stdout, but it can be
#             changed in order to redirect the log to a logfile.
#         """
#         output_stream = stream
# 
# 
#     @staticmethod
#     def add(msg, severity=Severity.INFO):
#         """ Adds a message with the given severity to the log. This message will be written
#             to the log output stream, which by default is sys.stdout. A newline character
#             will be added to the message by default.
#         """
#         if severity >= Log.min_severity:
#             ts = time.strftime("%Y-%m-%d %H:%M:%S", time.gmtime())
#             Log.output_stream.write(ts + ' [' + str(severity) + ']\t' + msg + '\n')
#             Log.output_stream.flush()
# 
# Path: pathpy/Log.py
# class Severity(enum.IntEnum):
#     """ An enumeration that can be used to indicate
#         the severity of log messages, and which can be
#         used tpo filter messages based on severities.
#     """
# 
#     ## Error messages
#     ERROR = 4
# 
#     ## Warning messages
#     WARNING = 3
# 
#     ## Informational messages (default minimum level)
#     INFO = 2
# 
#     ## Messages regarding timing and performance
#     TIMING = 1
# 
#     ## Debug messages (really verbose)
#     DEBUG = 0


# Path: pathpy/HigherOrderNetwork.py
import collections as _co
import bisect as _bs
import itertools as _iter
import numpy as _np 
import scipy.sparse as _sparse
import scipy.sparse.linalg as _sla
import scipy.linalg as _la
import scipy as _sp
from pathpy.Log import Log
from pathpy.Log import Severity

    def getDoF(self, assumption="paths"):
        """
        Calculates the degrees of freedom (i.e. number of parameters) of 
        this k-order model. Depending on the modeling assumptions, this either
        corresponds to the number of paths of length k in the first-order network 
        or to the number of all possible k-grams. The degrees of freedom of a model 
        can be used to assess the model complexity when calculating, e.g., the 
        Bayesian Information Criterion (BIC).

        @param assumption: if set to 'paths', for the degree of freedon calculation in the BIC, 
            only paths in the first-order network topology will be considered. This is 
            needed whenever we are interested in a modeling of paths in a given network topology.
            If set to 'ngrams' all possible n-grams will be considered, independent of whether they 
            are valid paths in the first-order network or not. The 'ngrams' and the 'paths' assumption 
            coincide if the first-order network is fully connected.
        """
        assert assumption == 'paths' or assumption == 'ngrams', 'Error: Invalid assumption'
        
        if assumption == 'paths':            
            return self.dof_paths
        else:            
            return self.dof_ngrams   


    def getDistanceMatrix(self):
        """
        Calculates shortest path distances between all pairs of 
        higher-order nodes using the Floyd-Warshall algorithm.
        """


        Log.add('Calculating distance matrix in higher-order network (k = ' + str(self.order) + ') ...', Severity.INFO)