import math
import naivelog
from collections import namedtuple

InputLink = namedtuple("InputLink", ["block", "output_port"])
Signal = namedtuple("Signal", ["time", "value"])
def enum(**enums):
            return type('Enum', (), enums)
level = enum(WARNING=1, ERROR=2, FATAL=3)	
epsilon = 0.001		

class BaseBlock:
    """
    A base class for all types of basic blocks
    """
    def __init__(self, name, input_ports, output_ports):
        self.setBlockName(name)

        #The output signals produced by this block is encoded as a dictionary.
        #The key of this dictionary is the name of the output port.
        #Each element of the dictionary contains an ordered list of values.
        self.__signals = dict()
        for output_port in output_ports:
            self.__signals[output_port] = []
        
        self._output_ports = output_ports
        
        #The input links produced by this block is encoded as a dictionary.
        #The key of this dictionary is the name of the input port.
        #Each element of the dictionary contains
        #an tuple of the block and the output name of the other block.
        self._linksIn = dict()

        #The list of possible input ports
        self.__nameLinks = input_ports
        #In wich CBD the baseblock is situated
        self._parent = None
    
    def getOutputPorts(self):
        return self._output_ports
    
    def resetSignals(self):
        for output_port in self.__signals:
            self.__signals[output_port] = []
    
    def getBlockName(self):
        return self.__block_name

    def setBlockName(self, block_name):
        self.__block_name = block_name

    def setParent(self, parent):
        self._parent = parent

    def getBlockType(self):
        return self.__class__.__name__

    def getLinksIn(self):
        return self._linksIn

    def getOutputNameOfInput(self, inputBlock):
        return [ y for (x,y) in self._linksIn.iteritems() if y.block == inputBlock ][0].output_port

    def getInputName(self, inputBlock):
        return [ x for (x,y) in self._linksIn.iteritems() if y.block == inputBlock ]

    def getClock(self):
        return self._parent.getClock()

    def appendToSignal(self, value, name_output = None):
        name_output = "OUT1" if name_output == None else name_output
        assert name_output in self.__signals.keys()
        self.__signals[name_output].append(Signal(self.getClock().getTime(), value))

    def getSignal(self, name_output = None):
        name_output = "OUT1" if name_output == None else name_output
        assert name_output in self.__signals.keys()
        return self.__signals[name_output] if name_output != None else self.__signals["OUT1"]

    def getDependencies(self, curIteration):
        return list(set([ tup.block for tup in self._linksIn.values() ]))

    def getBlockConnectedToInput(self, input_port):
        return self._linksIn[input_port]

    def getInputSignal(self, curIteration, input_port = None):
        """
        Returns the signal sent out by the input block (IN1 if none given,
        at the last time if no curIteration is given).
        """
        input_port = "IN1" if input_port == None else input_port
        curIteration = -1 if curIteration == None else curIteration

        (incoming_block, out_port_name) = self._linksIn[input_port]
        return incoming_block.getSignal(out_port_name)[curIteration]

    def compute(self, curIteration):
        exit("BaseBlock has nothing to compute")

    def linkInput(self, in_block, name_input, name_output):
        """
        linkInput will link the output of the from_block to the input of the to_block
        -if no name_input was given for the to_block, we will derive the right input,
        by checking which input IN has nothing connected to it yet
        -if no name_output was given we use the first OUT output
        """
        name_output = "OUT1" if name_output == None else name_output
        if name_input != None:
            assert name_input in self.__nameLinks
            self._linksIn[name_input] = InputLink(in_block, name_output)
        else:
            i = 1
            while True:
                nextIn = "IN" + str(i)
                if nextIn in self.__nameLinks:
                    if not nextIn in self._linksIn:
                        self._linksIn[nextIn] = InputLink(in_block, name_output)
                        return
                else:
                    exit("There are no open IN inputs left in block %s" % self.getBlockName())
                i += 1

    def __repr__(self):
        repr = self.getBlockName() + ":" + self.getBlockType() + "\n"
        if len(self._linksIn) == 0:
            repr+= "  No incoming connections to IN ports\n"
        else:
            for (key, (in_block, out_port)) in self._linksIn.iteritems():
                repr += "In input " + key + ":  IN <- " + in_block.getBlockName() + ":" + in_block.getBlockType() + "\n"
        return repr

class ConstantBlock(BaseBlock):
    """
    The constant block will always output its constant value
    """
    def __init__(self, block_name, value=0.0):
        BaseBlock.__init__(self, block_name, [], ["OUT1"])
        self.__value = value

    def getValue(self):
        return self.__value

    def setValue(self, value):
        self.__value = value

    def compute(self, curIteration):
        self.appendToSignal(self.getValue())

    def __repr__(self):
        return BaseBlock.__repr__(self) + "  Value = " + str(self.getValue()) + "\n"

class NegatorBlock(BaseBlock):
    """
    The negator block will output the value of the input multiplied with -1
    """
    def __init__(self, block_name):
        BaseBlock.__init__(self, block_name, ["IN1"], ["OUT1"])

    def compute(self, curIteration):
        self.appendToSignal(-self.getInputSignal(curIteration).value)

class InverterBlock(BaseBlock):
    """
    The invertblock will output 1/IN
    """
    def __init__(self, block_name):
        BaseBlock.__init__(self, block_name, ["IN1"], ["OUT1"])

    def compute(self, curIteration):
        self.appendToSignal(1.0/self.getInputSignal(curIteration).value)

class AdderBlock(BaseBlock):
    """
    The adderblock will add the 2 inputs
    """
    def __init__(self, block_name):
        BaseBlock.__init__(self, block_name, ["IN1", "IN2"], ["OUT1"])

    def	compute(self, curIteration):
        self.appendToSignal(self.getInputSignal(curIteration, "IN1").value + self.getInputSignal(curIteration, "IN2").value)

class ProductBlock(BaseBlock):
    """
    The product block will multiply the two inputs
    """
    def __init__(self, block_name):
        BaseBlock.__init__(self, block_name, ["IN1", "IN2"], ["OUT1"])

    def	compute(self, curIteration):
        self.appendToSignal(self.getInputSignal(curIteration, "IN1").value * self.getInputSignal(curIteration, "IN2").value)

class GenericBlock(BaseBlock):
    """
    The generic block will evaluate the operator on the input
    operator is the name (a string) of a Python function from the math library
    which will be called when the block is evaluated
    by default, initialized to None
    """
    def __init__(self, block_name, block_operator=None):
        # operator is the name (a string) of a Python function from the math library
        BaseBlock.__init__(self, block_name, ["IN1"], ["OUT1"])
        self.__block_operator = block_operator

    def getBlockOperator(self):
        return self.__block_operator

    def compute(self, curIteration):
        operator = getattr(math, self.getBlockOperator())
        self.appendToSignal(operator(self.getInputSignal(curIteration).value))

    def __repr__(self):
        repr = BaseBlock.__repr__(self)
        if self.__block_operator == None:
            repr += "  No operator given\n"
        else:
            repr += "  Operator :: " + self.__block_operator + "\n"
        return repr

class RootBlock(BaseBlock):
    """
    A basic block that computes the IN2-th root from IN1
    """
    def __init__(self, block_name):
        BaseBlock.__init__(self, block_name, ["IN1", "IN2"], ["OUT1"])

    def compute(self, curIteration):
        self.appendToSignal(pow(self.getInputSignal(curIteration, "IN1").value, 1.0/self.getInputSignal(curIteration, "IN2").value))

class ModuloBlock(BaseBlock):
    """
    A basic block that computes the IN1 modulo IN3
    """
    def __init__(self, block_name):
        BaseBlock.__init__(self, block_name, ["IN1", "IN2"], ["OUT1"])

    def compute(self, curIteration):
        self.appendToSignal(math.fmod(self.getInputSignal(curIteration, "IN1").value, self.getInputSignal(curIteration, "IN2").value))

class DelayBlock(BaseBlock):
    """
    A delay block that takes the last value from the list
    IC: Initial Condition
    """
    def __init__(self, block_name):
        BaseBlock.__init__(self, block_name, ["IN1", "IC"], ["OUT1"])
        self.__values = []

    def getDependencies(self, curIteration):
        if curIteration == 0:
            return [self._linksIn["IC"].block]
        return []
    
    def resetSignals(self):
        BaseBlock.resetSignals(self)
        self.__values = []
    
    def compute(self, curIteration):
        if curIteration == 0:
            outputValue = self.getInputSignal(curIteration, "IC")
            self.appendToSignal(outputValue.value)
        else:
            assert curIteration == len(self.__values)+1
            (incoming_block, out_port_name) = self._linksIn["IN1"]
            self.__values.append(incoming_block.getSignal(out_port_name)[curIteration-1])
            self.appendToSignal(self.__values[curIteration-1].value)
    
class InputPortBlock(BaseBlock):
    """
    The input port of a CBD
    """
    def __init__(self, block_name, parent):
        BaseBlock.__init__(self, block_name, [], ["OUT1"])
        self.parent = parent

    def	compute(self, curIteration):
        self.appendToSignal(self.parent.getInputSignal(curIteration, self.getBlockName()).value)

class OutputPortBlock(BaseBlock):
    """
    The output port of a CBD
    """
    def __init__(self, block_name, parent):
        BaseBlock.__init__(self, block_name, ["IN1"], ["OUT1"])
        self.parent = parent

    def	compute(self, curIteration):
        self.appendToSignal(self.getInputSignal(curIteration, "IN1").value)

class WireBlock(BaseBlock):
    """
    When a CBD gets flattened, the port blocks will be replaced by a wire block
    """
    def __init__(self, block_name):
        BaseBlock.__init__(self, block_name, ["IN1"], ["OUT1"])

    def	compute(self, curIteration):
        self.appendToSignal(self.getInputSignal(curIteration, "IN1").value)

class TimeBlock(BaseBlock):
    """
    Outputs the current time of the simulation
    """
    def __init__(self, block_name):
        BaseBlock.__init__(self, block_name, [], ["OUT1"])

    def	compute(self, curIteration):
        self.appendToSignal(self.getClock().getTime())

class LessThanBlock(BaseBlock):
    """
    A simple block that will test if the IN1 is smaller than IC (output == 1 if true else 0)
    """
    def __init__(self, block_name):
        BaseBlock.__init__(self, block_name, ["IC", "IN1"], ["OUT1"])

    def	compute(self, curIteration):
        gisv = lambda s: self.getInputSignal(curIteration, s).value
        self.appendToSignal(1 if gisv("IN1") < gisv("IC") else 0)

class EqualsBlock(BaseBlock):
    """
    A simple block that will test if the IN1 is equal to IC (output == 1 if true else 0)
    """
    def __init__(self, block_name):
        BaseBlock.__init__(self, block_name, ["IC", "IN1"], ["OUT1"])

    def	compute(self, curIteration):
        gisv = lambda s: self.getInputSignal(curIteration, s).value
        self.appendToSignal(1 if gisv("IN1") == gisv("IC") else 0)

class NotBlock(BaseBlock):
    """
    A simple Not block that will set a 0 to 1 and vice versa
    """
    def __init__(self, block_name):
        BaseBlock.__init__(self, block_name, ["IN1"], ["OUT1"])

    def	compute(self, curIteration):
        result = 0 if self.getInputSignal(curIteration, "IN1").value else 1
        self.appendToSignal(result)    

class OrBlock(BaseBlock):
    """
    A simple Or block with possibly multiple inputlines
    """
    def __init__(self, block_name, numberOfInputs=2):
        BaseBlock.__init__(self, block_name, ["IN{0}".format(i) for i in xrange(1,numberOfInputs+1)], ["OUT1"])
        self.__numberOfInputs = numberOfInputs

    def	compute(self, curIteration):
        result = 0
        for i in xrange(1, self.__numberOfInputs+1):
            result = result or self.getInputSignal(curIteration, "IN"+str(i)).value
        self.appendToSignal(result)    

class AndBlock(BaseBlock):
    """
    A simple And block with possibly multiple inputlines
    """
    def __init__(self, block_name, numberOfInputs=2):
        BaseBlock.__init__(self, block_name, ["IN{0}".format(i) for i in xrange(1,numberOfInputs+1)], ["OUT1"])
        self.__numberOfInputs = numberOfInputs

    def	compute(self, curIteration):
        result = 1
        for i in xrange(1, self.__numberOfInputs+1):
            result = result and self.getInputSignal(curIteration, "IN"+str(i)).value
        self.appendToSignal(result)

class SequenceBlock(BaseBlock):
    """
    A simple Sequence block: block initializes signal input with given sequence
    Use only for the tests please
    """
    def __init__(self, block_name, sequence):
        BaseBlock.__init__(self, block_name, [], ["OUT1"])
        self.__sequence = sequence

    def	compute(self, curIteration):
        if len(self.__sequence) < curIteration:
            self.__logger.fatal("Sequence is not long enough")
        self.appendToSignal(self.__sequence[curIteration])

class LoggingBlock(BaseBlock):
    """
    A simple Logging block
    """
    def __init__(self, block_name, string, lev = level.WARNING):
        BaseBlock.__init__(self, block_name, ["IN1"], [])
        self.__string = string
        self.__logger = naivelog.getLogger("WarningLog")
        self.__lev = lev

    def	compute(self, curIteration):
        if self.getInputSignal(curIteration, "IN1").value == 1:
            if self.__lev == level.WARNING:
                self.__logger.warning("Time " + str(self.getClock().getTime()) + ": " + self.__string)
            elif self.__lev == level.ERROR:
                self.__logger.error("Time " + str(self.getClock().getTime()) + ": " + self.__string)
            elif self.__lev == level.FATAL:
                self.__logger.fatal("Time " + str(self.getClock().getTime()) + ": " + self.__string)

class Clock:
    """
    The clock of the simulation
    delta_t is the timestep of the simulation
    """
    def __init__(self, delta_t):
        self.__delta_t = delta_t
        self.__time = 0.0

    def getTime(self):
        return self.__time

    def step(self):
        self.__time = self.__time + self.__delta_t

    def setDeltaT(self, new_delta_t):
        self.__delta_t = new_delta_t

    def getDeltaT(self):
        return self.__delta_t

class CBD(BaseBlock):
    """
    The CBD class, contains an entire Causal Block Diagram
    Call the run function to simulate the model.
    """
    def __init__(self, block_name, input_ports = None, output_ports = None):
        input_ports = input_ports if input_ports != None else []
        output_ports = output_ports if output_ports != None else []
        BaseBlock.__init__(self, block_name, input_ports, output_ports)
        #The blocks in the CBD will be stored both
        #-as an ordered list __blocks and
        #-as a dictionary __blocksDict with the blocknames as keys
        #for fast name-based retrieval and to ensure block names are unique within a single CBD
        self.__blocks = []
        self.__blocksDict = {}
        self.__clock = None
        self.__deltaT = None
        self.__logger = naivelog.getLogger("CBD")

        for input_port in input_ports:
            self.addBlock(InputPortBlock(input_port, self))

        for output_port in output_ports:
            self.addBlock(OutputPortBlock(output_port, self))

    def getTopCBD(self):
        return self if self._parent == None else self._parent.getTopCBD()

    def setBlocks(self, blocks):
        # blocks must be a list of BaseBlock (subclass) instances
        assert type(blocks) == list, ("CBD.setBlocks() takes a list as argument, not a %s" % type(blocks))
        for block in blocks:
            assert isinstance(block, BaseBlock), "CBD.setBlocks() takes a list of BaseBlock (subclass) instances"

    def getBlocks(self):
        return self.__blocks

    def getBlockByName(self, name):
        return self.__blocksDict[name]
    
    def setClock(self, clock):
        self.__clock = clock;
    
    def getClock(self):
        return self.__clock if self._parent == None else self._parent.getClock()

    def setDeltaT(self, deltaT):
        self.__deltaT = deltaT

    def addBlock(self, block):
        """
        Add a block to the CBD model
        """
        assert isinstance(block, BaseBlock), "Can only add BaseBlock (subclass) instances to a CBD"
        block.setParent(self)

        if not self.__blocksDict.has_key(block.getBlockName()):
            self.__blocks.append(block)
            self.__blocksDict[block.getBlockName()] = block
        else:
            print("Warning: did not add this block as it has the same name %s as an existing block" % block.getBlockName())

    def removeBlock(self, block):
        assert isinstance(block, BaseBlock), "Can only delete BaseBlock (subclass) instances to a CBD"

        if self.__blocksDict.has_key(block.getBlockName()):
            self.__blocks.remove(block)
            del self.__blocksDict[block.getBlockName()]
        else:
            exit("Warning: did not remove this block %s as it was not found" % block.getBlockName())

    def addConnection(self, from_block, to_block, input_port_name = None, output_port_name = None):
        """
        Add a connection between from_block with input_port_name to to_block with outport_port_name
        """
        if type(from_block) == str:
            from_block = self.getBlockByName(from_block)
        if type(to_block) == str:
            to_block = self.getBlockByName(to_block)
        to_block.linkInput(from_block, input_port_name, output_port_name)

    def __repr__(self):
        repr = BaseBlock.__repr__(self)
        repr += "\n"
        for block in self.getBlocks():
            repr+= block.__repr__()
        return repr

    def dump(self):
        print("=========== Start of Model Dump ===========")
        print(self)
        print("=========== End of Model Dump =============\n")

    def dumpSignals(self):
        print("=========== Start of Signals Dump ===========")
        for block in self.getBlocks():
            print("%s:%s" % (block.getBlockName(), block.getBlockType()))
            print(str(block.getSignal()) + "\n")
        print("=========== End of Signals Dump =============\n")
    
    def resetSignals(self):
        for block in self.getBlocks():
            block.resetSignals()
    
    def getSignal(self, name_output = None):
        name_output = "OUT1" if name_output == None else name_output
        portBlock = self.getBlockByName(name_output)
        assert portBlock != None
        return portBlock.getSignal("OUT1")
    
    
class AddOneBlock(CBD):
    """
    Block adds a one to the input (used a lot for mux)
    """
    def __init__(self, block_name, faultOrder=3):
        CBD.__init__(self, block_name, ["IN1"], ["OUT1"])
        self.addBlock(ConstantBlock(block_name="OneConstant", value=1))
        self.addBlock(AdderBlock("PlusOne"))
        self.addConnection("IN1", "PlusOne")
        self.addConnection("OneConstant", "PlusOne")
        self.addConnection("PlusOne", "OUT1")

class DerivatorBlock(CBD):
    """
    The derivator block is a CBD that calculates the derivative
    """
    def __init__(self, block_name):
        CBD.__init__(self, block_name, ["IN1", "delta_t", "IC"], ["OUT1"])
        self.addBlock(ProductBlock(block_name="multIc"))
        self.addBlock(NegatorBlock(block_name="neg1"))
        self.addBlock(AdderBlock(block_name="sum1"))
        self.addBlock(DelayBlock(block_name="delay"))
        self.addBlock(NegatorBlock(block_name="neg2"))
        self.addBlock(AdderBlock(block_name="sum2"))
        self.addBlock(ProductBlock(block_name="mult"))
        self.addBlock(InverterBlock(block_name="inv"))
        
        self.addConnection("IC", "multIc")
        self.addConnection("delta_t", "multIc")
        self.addConnection("multIc", "neg1")
        self.addConnection("neg1", "sum1")
        self.addConnection("IN1", "sum1")
        self.addConnection("sum1", "delay", input_port_name="IC")
        self.addConnection("IN1", "delay", input_port_name="IN1")
        self.addConnection("delay", "neg2")
        self.addConnection("neg2", "sum2")
        self.addConnection("IN1", "sum2")
        self.addConnection("sum2", "mult")
        self.addConnection("delta_t", "inv")
        self.addConnection("inv", "mult")
        self.addConnection("mult", "OUT1")
        

class IntegratorBlock(CBD):
    """
    The integrator block is a CBD that calculates the integration
    """
    def __init__(self, block_name):
        CBD.__init__(self, block_name, ["IN1", "delta_t", "IC"], ["OUT1"])
        self.addBlock(ConstantBlock(block_name="zero", value=0))
        self.addBlock(DelayBlock(block_name="delayIn"))
        self.addBlock(ProductBlock(block_name="multDelta"))
        self.addBlock(DelayBlock(block_name="delayState"))
        self.addBlock(AdderBlock(block_name="sumState"))
        
        self.addConnection("zero", "delayIn", input_port_name="IC")
        self.addConnection("IN1", "delayIn", input_port_name="IN1")
        self.addConnection("delayIn", "multDelta")
        self.addConnection("delta_t", "multDelta")
        self.addConnection("multDelta", "sumState")
        self.addConnection("IC", "delayState", input_port_name="IC")
        self.addConnection("delayState", "sumState")
        self.addConnection("sumState", "delayState", input_port_name="IN1")
        self.addConnection("sumState", "OUT1")
        
        

""" This module implements a dependency graph
    @author: Marc Provost
    @organization: McGill University
    @license: GNU General Public License
    @contact: marc.provost@mail.mcgill.ca
"""

import copy
class DepNode:
    """ Class implementing a node in the dependency graph.
    """

    def __init__(self, object):
        """ DepNode's constructor.
                @param object: Reference to a semantic object identifying the node
                @type object: Object
        """
        self.__object = object
        self.__isMarked	 = False

    def mark(self):
        self.__isMarked = True

    def unMark(self):
        self.__isMarked = False

    def isMarked(self):
        return self.__isMarked

    def getMappedObj(self):
        return self.__object

    def __repr__(self):
        return "DepNode :: "+str(self.__object)

class DepGraph:
    """ Class implementing dependency graph.
    """

    def __init__(self):
        """ DepGraph's constructor.
        """
        #Dict holding a mapping "Object -> DepNode"
        self.__semanticMapping = {}

        #map object->list of objects depending on object
        self.__dependents = {}
        #map object->list of objects that influences object
        self.__influencers = {}

    def __repr__(self):
        repr = "Dependents: \n"
        for dep in self.__dependents:
            repr += dep.getBlockName() + ":" + str(self.__dependents[dep]) + "\n"
        repr += "Influencers: \n"
        for infl in self.__influencers:
            repr += infl.getBlockName() + ":" + str(self.__influencers[infl]) + "\n"
        return repr

    def addMember(self, object):
        """ Add an object mapped to this graph.
                @param object: the object to be added
                @type object: Object
                @raise ValueError: If object is already in the graph
        """
        if not self.hasMember(object):
            if not isinstance(object, CBD):
                node = DepNode(object)
                self.__dependents[object] = []
                self.__influencers[object] = []
                self.__semanticMapping[object] = node
            else:
                for block in object.getBlocks():
                    self.addMember(block)
        else:
            raise ValueError("Specified object is already member of this graph")

    def hasMember(self, object):
        return self.__semanticMapping.has_key(object)

    def removeMember(self, object):
        """ Remove a object from this graph.
                @param object: the object to be removed
                @type object: Object
                @raise ValueError: If object is not in the graph
        """
        if self.hasMember(object):
            for dependent in self.getDependents(object):
                self.__influencers[dependent].remove(object)
            for influencer in self.getInfluencers(object):
                self.__dependents[influencer].remove(object)

            del self.__dependents[object]
            del self.__influencers[object]
            del self.__semanticMapping[object]
        else:
            raise ValueError("Specified object is not member of this graph")

    def setDependency(self, dependent, influencer, curIt):
        """
            Creates a dependency between two objects.
                @param dependent: The object which depends on the other
                @param influcencer: The object which influences the other
                @type dependent: Object
                @type dependent: Object
                @raise ValueError: if depedent or influencer is not member of this graph
                @raise ValueError: if the dependency already exists
        """

        # Link CBD outputs
        if isinstance(influencer, CBD):
            # When there is more than one connection from a CBD to one and the same block,
            # more than one dependency should be set, as there is more than one underlying
            # output block
            for output_port in [ y.output_port for (x,y) in dependent.getLinksIn().iteritems() if y.block == influencer ]:
                self.setDependency(dependent, influencer.getBlockByName(output_port), curIt)
            return

        # Link CBD inputs
        if isinstance(dependent, CBD):
            cbd = dependent
            directlyConnected = influencer.parent if isinstance(influencer, OutputPortBlock) else influencer
            inputnames = dependent.getInputName(directlyConnected)

            # When one influencer has multiple connections to this CBD, call this function once fo
            for inputname in inputnames:
                inputtingBlock = dependent.getBlockByName(inputname)
                thisdep = inputtingBlock
                self.setDependency(thisdep, influencer, curIt)
            return

        if self.hasMember(dependent) and self.hasMember(influencer):
            if not influencer in self.__influencers[dependent] and\
                 not dependent in self.__dependents[influencer]:
                self.__influencers[dependent].append(influencer)
                self.__dependents[influencer].append(dependent)
        else:
            if not self.hasMember(dependent):
                raise ValueError("Specified dependent object is not member of this graph")
            if not self.hasMember(influencer):
                print(influencer)
                raise ValueError("Specified influencer object is not member of this graph")

    def hasDependency(self, dependent, influencer):
        if self.hasMember(dependent) and self.hasMember(influencer):
            return influencer in self.__influencers[dependent] and\
                         dependent in self.__dependents[influencer]
        else:
            if not self.hasMember(dependent):
                raise ValueError("Specified dependent object is not member of this graph")
            if not self.hasMember(influencer):
                raise ValueError("Specified influencer object is not member of this graph")

    def unsetDependency(self, dependent, influencer):
        """ Removes a dependency between two objects.
                @param dependent: The object which depends on the other
                @param influcencer: The object which influences the other
                @type dependent: Object
                @type dependent: Object
                @raise ValueError: if depedent or influencer is not member of this graph
                @raise ValueError: if the dependency does not exists
        """
        if self.hasMember(dependent) and self.hasMember(influencer):
            if influencer in self.__influencers[dependent] and\
                 dependent in self.__dependents[influencer]:
                self.__influencers[dependent].remove(influencer)
                self.__dependents[influencer].remove(dependent)
            else:
                raise ValueError("Specified dependency does not exists")
        else:
            if not self.hasMember(dependent):
                raise ValueError("Specified dependent object is not member of this graph")
            if not self.hasMember(influencer):
                raise ValueError("Specified influencer object is not member of this graph")

    def getDependents(self, object):
        if self.hasMember(object):
            return copy.copy(self.__dependents[object])
        else:
            raise ValueError("Specified object is not member of this graph")

    def getInfluencers(self, object):
        if self.hasMember(object):
            return copy.copy(self.__influencers[object])
        else:
            raise ValueError("Specified object is not member of this graph")

    def getStrongComponents(self, curIt = 1):
        return self.__strongComponents(curIt)

    def __getDepNode(self, object):
        if self.hasMember(object):
            return self.__semanticMapping[object]
        else:
            raise ValueError("Specified object is not a member of this graph")

    def __mark(self, object):
        self.__getDepNode(object).mark()

    def __unMark(self, object):
        self.__getDepNode(object).unMark()

    def __isMarked(self, object):
        return self.__getDepNode(object).isMarked()

    def __topoSort(self):
        """ Performs a topological sort on the graph.
        """
        for object in self.__semanticMapping.keys():
            self.__unMark(object)

        sortedList = []

        for object in self.__semanticMapping.keys():
            if not self.__isMarked(object):
                self.__dfsSort(object, sortedList)

        return sortedList

    def __dfsSort(self, object, sortedList):
        """ Performs a depth first search collecting
                the objects in topological order.
                @param object: the currently visited object.
                @param sortedList: partial sorted list of objects
                @type object: Object
                @type sortedList: list Of Object
        """

        if not self.__isMarked(object):
            self.__mark(object)

            for influencer in self.getInfluencers(object):
                self.__dfsSort(influencer, sortedList)

            sortedList.append(object)

    def __strongComponents(self, curIt):
        """ Determine the strong components of the graph
                @rtype: list of list of Object
        """
        strongComponents = []
        sortedList = self.__topoSort()

        for object in self.__semanticMapping.keys():
            self.__unMark(object)

        sortedList.reverse()

        for object in sortedList:
            if not self.__isMarked(object):
                component = []
                self.__dfsCollect(object, component, curIt)
                strongComponents.append(component)

        strongComponents.reverse()
        return strongComponents

    def __dfsCollect(self, object, component, curIt):
        """ Collects objects member of a strong component.
                @param object: Node currently visited
                @param component: current component
                @type object: Object
                @type component: List of Object
        """
        if not self.__isMarked(object):
            self.__mark(object)

            for dependent in self.getDependents(object):
                self.__dfsCollect(dependent, component, curIt)

            component.append(object)













