def todo(fn):
def skip(reason):
def _log(fn, message):
def canonical_cnf(be):
def current_directory(what):
    def __init__(self, testcase, location, model):
    def __enter__(self):
    def __exit__(self, type_, value, traceback):
class Configure: def init_nusmv(collecting=True):
    """
    Initialize NuSMV. Must be called only once before calling
    :func:`deinit_nusmv`.

    :param collecting: Whether or not collecting pointer wrappers to free them
                       before deiniting nusmv.

    .. warning: Deactivating the collection of pointer wrappers may provoke
                segmentation faults when deiniting nusmv without correctly
                freeing all pointer wrappers in advance.
                On the other hand, collection may blow memory.

    """
    global __collector, __collecting
    if __collector is not None:
        raise NuSMVInitError("Cannot initialize NuSMV twice.")
    else:
        __collecting = collecting
        __collector = set()
        nscinit.NuSMVCore_init_data()
        nscinit.NuSMVCore_init(None, 0)  # No addons specified
        nscinit.NuSMVCore_init_cmd_options()

        # Set NuSMV in interactive mode to avoid fast termination when errors
        # nsopt.unset_batch(nsopt.OptsHandler_get_instance())

        # Initialize option commands (set, unset)
        # to be able to set parser_is_lax
        nsopt.init_options_cmd()
        nscmd.Cmd_SecureCommandExecute("set parser_is_lax")

        return _PyNuSMVContext() def deinit_nusmv(ddinfo=False):
    """
    Quit NuSMV. Must be called only once, after calling :func:`init_nusmv`.

    :param ddinfo: Whether or not display Decision Diagrams statistics.

    """
    global __collector
    if __collector is None:
        raise NuSMVInitError(
            "Cannot deinitialize NuSMV before initialization.")
    else:
        # Apply Python garbage collection first,
        # then collect every pointer wrapper
        # that is not yet collected by Python GC
        from . import glob

        # Print statistics on stdout about DDs handled by the main DD manager.
        if ddinfo:
            try:
                manager = glob.prop_database().master.bddFsm.bddEnc.DDmanager
                nsdd.dd_print_stats(manager._ptr, nscinit.cvar.nusmv_stdout)
            except PyNuSMVError:
                pass

        glob._reset_globals()

        # First garbage collect with Python
        gc.collect()
        # Then garbage collect with PyNuSMV
        for elem in __collector:
            elem._free()
        __collector = None
        nscinit.NuSMVCore_quit() def load_from_file(filepath):
    """
    Load a model from an SMV file and store it in global data structures.

    :param filepath: the path to the SMV file
    :type filepath: str

    """
    # Check file
    if not os.path.exists(filepath):
        raise IOError("File {} does not exist".format(filepath))

    # Check cmps. Need reset_nusmv if a model is already read
    if nscompile.cmp_struct_get_read_model(global_compile_cmps()):
        raise NuSMVModelAlreadyReadError("A model is already read.")

    # Set the input file
    nsopt.set_input_file(nsopt.OptsHandler_get_instance(), filepath)

    # Call the parser
    # ret = 0 => OK
    # ret = 1 => syntax error (and there are registered syntax errors)
    # ret = 2 => lexer error
    ret = nsparser.Parser_ReadSMVFromFile(filepath)
    if ret == 2:
        # ret = 2 means lexer error
        raise NuSMVLexerError("An error with NuSMV lexer occured.")

    # When parsing a model with parser_is_lax enabled (this is the case
    # since this is enabled in init_nusmv), the parser gets
    # as many correct parts of the model as possible and build a partial
    # model with it.

    # Raise exceptions if needed
    errors = nsparser.Parser_get_syntax_errors_list()
    if errors is not None:
        raise NuSMVParsingError.from_nusmv_errors_list(errors)

    # Update cmps
    nscompile.cmp_struct_set_read_model(global_compile_cmps()) def go_bmc(force=False):
    """
    Performs all the necessary steps to use loaded model and be able to perform
    bmc related operations on it.
    
    :raises NuSMVNoReadModelError: if no module was read (:func:`pynusmv.glob.load`) 
      before this method was called. 
    """
    cmp = glob.global_compile_cmps() 
    if not _compile.cmp_struct_get_read_model(cmp):
        raise NuSMVNoReadModelError("No read model.")

    # Check cmps and perform what is needed
    if not _compile.cmp_struct_get_flatten_hrc(cmp):
        glob.flatten_hierarchy()
    if not _compile.cmp_struct_get_encode_variables(cmp):
        glob.encode_variables()
    if not _compile.cmp_struct_get_build_bool_model(cmp):
        glob.build_boolean_model(force=force)
    if not _compile.cmp_struct_get_bmc_setup(cmp):
        bmc_setup(force=force) def bmc_exit():
    """
    Releases all resources associated to the bmc model manager. 
    If you want to do bmc again after calling this, you will have to call
    :func:`bmc_setup` or :func:`go_bmc` again.
    """  
    global __be_fsm
    _bmc.Bmc_Quit()
    __be_fsm = None
    
    _lower.MEMOIZER_clear() class Be:
    """
    This is the interface of the boolean expression type.
    For obvious reasons, the function names have been kept as close to its
    BDD counterpart. The 'dsl' has also been kept. Hence:
    
        * ``a + b`` and ``a | b`` compute the disjunction of ``a`` and ``b``
        * ``a * b`` and ``a & b`` compute the conjunction of ``a`` and ``b``
        * ``~a`` and ``-a`` compute the negation of ``a``
        * ``a - b`` computes ``a & ~b``
        * ``a ^ b`` computes the exclusive-OR (XOR) of ``a`` and ``b``

    However, no comparison operators such as (lt, ge, ...) are provided.
    
    .. note::
        
        For the sake of compactness and efficient memory use, the boolean
        expressions (Be) are encoded in the form of reduced boolean circuits
        (directed acyclic graphs) because this representation is up to 
        exponentially smaller than the classic tree representation. See slides
        9-10 and 19 of http://disi.unitn.it/~rseba/DIDATTICA/fm2016/03_TEMPORAL_LOGICS_SLIDES.pdf
        to get more information about this.
        
        However, the details of the reduced boolean circuit are (so far) not  
        accessible throught PyNuSMV since it was considered as a too low level
        concern.
    """

    def __init__(self, ptr, be_manager):
        """
        Creates a new boolean expression (BE) from the given ``ptr``

        Args:
            :param ptr:        a pointer to a NuSMV structure representing a be_ptr
            :type  ptr:        be_ptr
            :param be_manager: the BeManager responsible for this object
            :type  be_manager: BeManager
            :param freeit:     A flag telling whether or not this object has to be
                               automatically freed by the system.
                               (more info in ``PointerWrapper``)
        """
        assert (ptr is not None)
        assert (be_manager is not None)
        self._ptr = ptr
        self._manager = be_manager

    def __hash__(self):
        """
        Makes this object hashable. 
        
        .. warning::
        
            Beware it uses the pointer to implement the hashing function.  
            So it is IDENTITY dependent (in C) and not value dependant.
        
        :return: an object that can serve as key to perform the lookup in a dict.
        """
        return self._ptr

    def __eq__(self, other):
        """
        Equality test between two objects. 
        
        .. warning::
        
            Beware it uses the pointer to implement the hashing function.  
            So it is IDENTITY dependent (in C) and not value dependant.
        
        :return: True iff the two object are the same
        """
        return self._ptr == other._ptr 

    # ==============================================================================
    # ===== Static methods to build true false =====================================
    # ==============================================================================
    @staticmethod
    def true(be_manager) -> 'Be':
        """
        Creates a constant with the meaning 'True'

        :param be_manager: the manager responsible for this expression
        :type  be_manager: BeManager
        :return: a constant boolean expression meaning True
        """
        return Be(_be.Be_Truth(be_manager._ptr), be_manager)

    @staticmethod
    def false(be_manager) -> 'Be':
        """
        Creates a constant with the meaning 'False'

        :param be_manager: the manager responsible for this expression
        :type  be_manager: BeManager
        :return: a constant boolean expression meaning False
        """
        return Be(_be.Be_Falsity(be_manager._ptr), be_manager)

    @staticmethod
    def if_then_else(be_manager, _if, _then, _else) -> 'Be':
        """
        Creates an if then else operation

        :param be_manager: the manager responsible for this expression
        :type  be_manager: BeManager
        :param        _if: the 'if' condition expression of the if then else
        :type         _if: Be
        :param      _then: the 'then' part of the expression
        :type       _then: Be
        :param      _else: the alternative
        :type       _else: Be
        :return: a constant boolean expression meaning True
        """
        return Be(_be.Be_Ite(be_manager._ptr, _if._ptr, _then._ptr, _else._ptr), be_manager)

    # ==============================================================================
    # ===== BE operations ==========================================================
    # ==============================================================================
    def is_true(self) -> 'bool':
        """
        Returns true iff the expression can be evaluated to True
        
        :return: true iff the expression can be evaluated to True
        """
        return bool(_be.Be_IsTrue(self._manager._ptr, self._ptr))

    def is_false(self) -> 'bool':
        """
        Returns true iff the expression can be evaluated to False
        
        :return: true iff the expression can be evaluated to False
        """
        return bool(_be.Be_IsFalse(self._manager._ptr, self._ptr))

    def is_constant(self) -> 'bool':
        """
        Returns true if the given be is a constant value, such as either False or True
        
        :return: true if the given be is a constant value, such as either False or True
        """
        return bool(_be.Be_IsConstant(self._manager._ptr, self._ptr))

    def not_(self) -> 'Be':
        """
        Negates the current expression
        
        :return: an expression (Be) corresponding to the negation of `self`
        """
        return Be(_be.Be_Not(self._manager._ptr, self._ptr), self._manager)

    def and_(self, other) -> 'Be':
        """
        Returns an expression (Be) corresponding to the conjunction of `self` and `other`
        
        :param other: a Be that will be conjuncted with self.
        :return: Returns an expression (Be) corresponding to the conjunction of `self` and `other`
        """
        return Be(_be.Be_And(self._manager._ptr, self._ptr, other._ptr), self._manager)

    def or_(self, other) -> 'Be':
        """
        Returns an expression (Be) corresponding to the disjunction of `self` and `other`
        
        :param other: a Be that will be disjuncted with self.
        :return: Returns an expression (Be) corresponding to the disjunction of `self` and `other`
        """
        return Be(_be.Be_Or(self._manager._ptr, self._ptr, other._ptr), self._manager)

    def xor(self, other) -> 'Be':
        """
        Returns an expression (Be) corresponding (`self` exclusive or `other`)
        
        :param other: a Be that will be xor'ed with self.
        :return: Returns an expression (Be) corresponding (`self` exclusive or `other`)
        """
        return Be(_be.Be_Xor(self._manager._ptr, self._ptr, other._ptr), self._manager)

    def imply(self, other) -> 'Be':
        """
        Returns an expression (Be) corresponding :math:`(self \\implies other)`. That is
        to say: :math:`(\\neg self \\vee other)` 
        
        :param other: a Be that will have to be implied by `self`
        :return: Returns an expression (Be) corresponding :math:`(self \\implies other)`
        """
        return Be(_be.Be_Implies(self._manager._ptr, self._ptr, other._ptr), self._manager)

    def iff(self, other) -> 'Be':
        """
        Returns an expression (Be) corresponding :math:`(self \\iff other)`. That is
        to say: :math:`(self \\implies other) \\wedge (other \\implies self)` 
        
        :param other: a Be that will have to be equivalent to `self`
        :return: Returns an expression (Be) corresponding :math:`(self \\iff other)`
        """
        return Be(_be.Be_Iff(self._manager._ptr, self._ptr, other._ptr), self._manager)

    def inline(self, add_conj):
        """
        Performs the inlining of this expression, either including or not the conjuction set.

        If add_conj is true, the conjuction set is included, otherwise only the inlined
        formula is returned for a lazy SAT solving.

        :param conj: a flag to tell whether or not to include the conjunction set
        :return: a copy of this expression with the inlining performed.
        """
        return Be(_be.Be_apply_inlining(self._manager._ptr, self._ptr, add_conj), self._manager)

    # ==============================================================================
    # ===== pythonic equivalent of the above =======================================
    # ==============================================================================
    def __neg__(self):
        """
        Negates the current expression (allows to write -self)
        
        :return: an expression (Be) corresponding to the negation of `self'
        """
        return self.not_()

    def __invert__(self):
        """
        Negates the current expression (allows to write ~self)
        
        :return: an expression (Be) corresponding to the negation of `self'
        """
        return self.not_()

    def __and__(self, other):
        """
        Returns an expression (Be) corresponding to the conjunction of `self' and `other'
        Allows to use the operator notation to write 
            `self' and `other'
            `self'  &  `other'
        
        :param other: a Be that will be conjuncted with self.
        :return: Returns an expression (Be) corresponding to the conjunction of `self' and `other'
        """
        return self.and_(other)
    
    def __mul__(self, other):
        """
        Returns an expression (Be) corresponding to the conjunction of `self' and `other'
        Allows to use the operator notation to write `self' * `other'
        
        :param other: a Be that will be conjuncted with self.
        :return: Returns an expression (Be) corresponding to the conjunction of `self' and `other'
        """
        return self.and_(other)
    
    def __or__(self, other):
        """
        Returns an expression (Be) corresponding to the disjunction of `self' and `other'
        Allows to use the operator notation to write 
            `self' or `other'
            `self' |  `other'
        
        :param other: a Be that will be disjuncted with self.
        :return: Returns an expression (Be) corresponding to the disjunction of `self' and `other'
        """
        return self.or_(other)
    
    def __add__(self, other):
        """
        Returns an expression (Be) corresponding to the disjunction of `self' and `other'
        Allows to use the operator notation to write  `self' + `other'
        
        :param other: a Be that will be disjuncted with self.
        :return: Returns an expression (Be) corresponding to the disjunction of `self' and `other'
        """
        return self.or_(other)
    
    def __xor__(self, other):
        """
        Returns an expression (Be) corresponding (`self' exclusive or `other')
        Allows to use the operator notation to write `self' ^ `other'
        
        :param other: a Be that will be xor'ed with self.
        :return: Returns an expression (Be) corresponding (`self' exclusive or `other')
        """
        return self.xor(other)

    def __sub__(self, other):
        """
        Returns an expression (Be) corresponding (`self' and not `other')
        Allows to use the operator notation to write `self' - `other'
        
        :param other: a Be that will be combined with self.
        :return: Returns an expression (Be) corresponding (`self' and not `other')
        """
        return self & ~(other)

    # ==============================================================================
    # ===== Conversions ============================================================
    # ==============================================================================
    ################################################################################
    # Hidden because it only returns an opaque pointer to self but casted to Rbc_t
    ################################################################################
    # def to_spec(self):
    #     """
    #     Converts a be to an external format (for instance RBC)
    # 
    #     :return: a specification corresponding to this expr
    #     """
    #     return _be.Be_Manager_Be2Spec(self._manager._ptr, self._ptr)
    ################################################################################

    def to_cnf(self, polarity=Polarity.POSITIVE):
        """
        Converts this boolean expression to a corresponding CNF

        .. note::
            By default, the POSITIVE polarity is used since it corresponds to
            what one would intuitively imagine when converting a formula to
            CNF. 

        'polarity' is used to determine if the clauses generated should
        represent the RBC positively, negatively, or both (1, -1 or 0
        respectively). For an RBC that is known to be true, the clauses
        that represent it being false are not needed (they would be removed
        anyway by propogating the unit literal which states that the RBC is
        true). Similarly for when the RBC is known to be false. This
        parameter is only used with the compact cnf conversion algorithm,
        and is ignored if the simple algorithm is used.
        
        :param polarity: the polarity of the expression
        :type  polarity: integer
        :return: a CNF equivalent of this boolean expression
        """
        return BeCnf(_be.Be_ConvertToCnf(self._manager._ptr, self._ptr, polarity), self._manager) class BeFsm(PointerWrapper):
    """
    This class wraps the public interface of a BE FSM as defined in NuSMV
    with the BeFsm_ptr type.
    
    Its role is to give an access to the properties of FSM as represented with
    boolean expressions (BE). For instance, it gives access to the initial states
    the invariants and the transition relation encoded in terms of BE. Moreover
    it gives the possibility to compute the synchronous product of two FSMs.  
    """
    
    def __init__(self, ptr, freeit=False):
        """
        Creates an instance of the BeFsm, using the NuSMV pointer. 
        :param ptr: the NuSMV pointer of type BeFsm_ptr
        :param freeit: a flag indicating whether or not the system resources 
          should be freed upon garbage collection.
        """
        super().__init__(ptr, freeit)
        
    def _free(self):
        """Frees the system resources associated with this object"""
        if self._freeit and self._ptr is not None:
            _be.BeFsm_destroy(self._ptr)
            self._freeit = False
            self._ptr    = None
    
    @staticmethod
    def global_master_instance():
        """
        :return: the boolean FSM in BE stored in the master prop. 
        :raises NuSMVBeFsmMasterInstanceNotInitializedError:
             when the global BE FSM is null in the global properties database 
             (ie when coi is enabled).
        """
        _pd  = _prop.PropPkg_get_prop_database()
        _fsm = _prop.PropDb_master_get_be_fsm(_pd)
        if _fsm is None:
            raise NuSMVBeFsmMasterInstanceNotInitializedError("""
                            BE fsm stored in master prop is not initialized.
                            You must either initialize the BE model (ie. go_bmc)
                            or disable COI.
                            """)
        return BeFsm(_fsm)
    
    @staticmethod
    def create(enc, init, invar, trans, fairness, freeit=False):
        """
        Creates a new BeFsm instance using the given encoder, initial states, 
        invariants, transition relation and list of fairness.
        
        :param enc: the encoder used to represent the variables of the model.
        :param init: the boolean expression representing the initial states of 
          the FSM
        :param invar: the boolean expression representing the invariants of the 
          model encoded in this FSM
        :pqram trans: the boolean expression representing the transition relation
          of the model encoded in this FSM
        :param fairness: a list of boolean expression representing the fairness
          constraints of the model. 
        :param freeit: a flag indicating whether or not the system resources 
          should be freed upon garbage collection.
          
        :return: a new instance of BeFsm that gets its init, invar, transition 
          relation and the list of fairness in Boolean Expression format
        """
        # very unlikely to be used directly
        return BeFsm(_be.BeFsm_create(enc._ptr, 
                                      init._ptr, 
                                      invar._ptr,
                                      trans._ptr,
                                      fairness._ptr))
    
    @staticmethod
    def create_from_sexp(enc, sexp_fsm):
        """
        Creates a new instance of BeFsm extracting the necessary information
        from the given `sexp_fsm` of type :class:`BoolSexpFsm <pynusmv.sexp.BoolSexpFsm>`
        
        :param enc: the encoder used to represent the variables of the model.
        :param sexp_fsm: the BoolSexpFsm which contains the automaton information
        """
        return BeFsm(_be.BeFsm_create_from_sexp_fsm(enc._ptr, sexp_fsm._ptr)) 
    
    def copy(self, freeit=True):
        """
        Creates a new independent copy of the FSM.
        :param freeit: a flag indicating whether or not the system resources 
        should be freed upon garbage collection.
        """
        return BeFsm(_be.BeFsm_copy(self._ptr), freeit=freeit)
    
    def __copy__(self):
        """Creates a new independent copy of the FSM"""
        return self.copy()
    
    @property
    def encoding(self):
        """The BE encoding of this FSM"""
        if not hasattr(self, "__encoding"):
            setattr(self, '__encoding', BeEnc(_be.BeFsm_get_be_encoding(self._ptr), freeit=False))
        return getattr(self, '__encoding')
    
    @property
    def init(self):
        """The BE representing the initial states of this FSM"""
        _expr = _be.BeFsm_get_init(self._ptr)
        return Be(_expr, self.encoding.manager) if _expr is not None else None
    
    @property
    def invariants(self):
        """The boolean expression representing the invariants of this FSM"""
        _expr = _be.BeFsm_get_invar(self._ptr)
        return Be(_expr, self.encoding.manager) if _expr is not None else None
    
    @property
    def trans(self):
        """
        The boolean expression representing the transition relation of the FSM
        
        .. note::
        
            Transition expression shifted at time zero is what brings you to 
            state one. Hence::
            
                shift_to_time(init, 0) & shift_to_time(trans, 0) == STATE_1 
        """
        _expr = _be.BeFsm_get_trans(self._ptr)
        return Be(_expr, self.encoding.manager) if _expr is not None else None
    
    @property
    def fairness_list(self):
        """
        The list of fairness constraints of this model encoded in BE format.
        
        .. note:: accessing this property is not free: use fairness_iterator
           instead if you don't need to manipulate the list as a list.
        """
        return [ i for i in self.fairness_iterator() ]
    
    def fairness_iterator(self):
        """:return: an iterator to iterate over the fairness list"""
        _ptr  = _be.BeFsm_get_fairness_list(self._ptr)
        _iter = NodeIterator.from_pointer(_ptr)
        for fairness in _iter:
            yield self._fairness_conversion(fairness)
    
    def _fairness_conversion(self, fairness):
        """
        Converts the given `fairness` into a Be representation.
        
        .. note::
            This function is present for purely technical reason: under the 
            hood, NuSMV encodes the fairness list as a NodeList however the
            'car' (value) of these nodes is nothing that can be understood 
            AST-wise. Indeed, the values are opaque pointers (be_ptr that is 
            to say void*) to a structure representing a Be. 
        """
        beptr = _be.node_ptr_to_be_ptr(_node.car(fairness._ptr))
        bexpr = Be(beptr, self.encoding.manager)
        return bexpr
    
    def apply_synchronous_product(self, other):
        """
        Apply the synchronous product between self and other modifying self.
        :param other: the other to compute the synchronous product with
        """
        _be.BeFsm_apply_synchronous_product(self._ptr, other._ptr)
        
    def __mul__(self, other):
        """
        :return: a new fsm corresponding to the synchronous product of self and
        other.
        :param other: the other to compute the synchronous product with
        """
        cpy = self.copy()
        cpy.apply_synchronous_product(other)
        return cpy
    
    def __imul__(self, other):
        """
        Apply the synchronous product between self and other modifying self.
        :param other: the other to compute the synchronous product with
        """
        self.apply_synchronous_product(other) class BmcModel:
    """
    The :class:`BmcModel` defines a wrapper providing an higher level interface
    to the BeFsm object. This is the object that must be used to generate the 
    LTL problems.
    """
    
    def __init__(self, be_fsm=None):
        """
        Creates a new instance from a given BeFsm.
        
        If `be_fsm` is None, the global master be_fsm is used.
        
        :param be_fsm: the fsm to wrap.
        """
        self._fsm = be_fsm if be_fsm is not None else bmcglob.master_be_fsm()
    
    @indexed.getter
    def init(self, time):
        """
        Retrieves the init states and compiles them into a BE at time `time`
        
        :param time: the time at which to consider the init.
        :return: a Be corresponding to the init states at time `time`
        :raises ValueError: if the specified time is negative.
        """
        if time < 0 :
            raise ValueError("Time cannot be negative")
        if time == 0:
            return Be(_bmc.Bmc_Model_GetInit0(self._fsm._ptr), self._fsm.encoding.manager)
        else:
            return Be(_bmc.Bmc_Model_GetInitI(self._fsm._ptr, time), self._fsm.encoding.manager)
       
    @indexed.getter 
    def invar(self, time):
        """
        Retrieves the invars and compiles them into a BE at the given time.
        
        :param time: the time at which to consider the invars.
        :return: a Be corresponding to the invar states at time `time`
        :raises ValueError: if the specified time is negative.
        """
        if time < 0 :
            raise ValueError("Time cannot be negative")
        return Be(_bmc.Bmc_Model_GetInvarAtTime(self._fsm._ptr, time), self._fsm.encoding.manager)
    
    @indexed.getter 
    def trans(self, time):
        """
        Retrieves the trans and compiles them into a BE at the given time.
        
        :param time: the time at which to consider the trans.
        :return: a Be corresponding to the trans at time `time`
        :raises ValueError: if the specified time is negative.
        """
        if time < 0 :
            raise ValueError("Time cannot be negative")
        return Be(_bmc.Bmc_Model_GetTransAtTime(self._fsm._ptr, time), self._fsm.encoding.manager)
    
    def unrolling(self, j, k):
        """
        Unrolls the transition relation from j to k, taking into account of 
        invars.
        
        :param j: the start time
        :param k: the end time
        :return: a Be representing the unrolling of the fsm from time i to k
        :raises ValueError: if one of the specified times (k,j) is negative and 
            when k < j
        """
        if j < 0 or k < 0: 
            raise ValueError("time must be positive")
        if k < j:
            raise ValueError("unrolling can only increase the amount of constraints")
        return Be(_bmc.Bmc_Model_GetUnrolling(self._fsm._ptr, j, k), self._fsm.encoding.manager)
    
    @indexed.getter
    def unrolling_fragment(self, i):
        """
        Generates the ith fragment of the transition relation unrolling. 
        (useful for incremental algorithms). Concretely, unrolls the transition 
        relation from i-1 to i
        
        :param i: the time index of the fragment to generate. 
        :return: a Be expression corresponding to the ith unrolling of the 
            transition relation.
        :raise ValueError: when `i` is negative
        """
        if i < 0:
            raise ValueError("time indices start at 0")
        ptr = _bmc.Bmc_Gen_UnrollingFragment(self._fsm._ptr, i)
        return Be(ptr, self._fsm.encoding.manager)
    
    def path(self, k, with_init=True):
        """
        Returns the path for the model from 0 to k. If the flag `with_init` is
        off, only the invariants are taken into account (and no init) otherwise
        both are taken into account.
        
        :param k: the end time
        :param with_init: a flag indicating whether or not to consider the init
        :retunr: a Be representing the paths in the model from times 0 to `k`.
        :raises ValueError: if the specified time k is negative.
        """
        if k < 0: 
            raise ValueError("time must be positive")
        if with_init:
            return Be(_bmc.Bmc_Model_GetPathWithInit(self._fsm._ptr, k), self._fsm.encoding.manager)
        else:
            return Be(_bmc.Bmc_Model_GetPathNoInit(self._fsm._ptr, k), self._fsm.encoding.manager)
    
    def fairness(self, k, l):
        """
        Generates and returns an expression representing all fairnesses in a 
        conjunctioned form.
        
        :param k: the maximum length of the considered problem
        :param l: the time when a loop may start
        
        :return:  an expression representing all fairnesses in a conjunctioned form.
        :raises ValueError: when the k and l parameters are incorrect (namely, when
            one says the loop must start after the problem bound).
        """
        if k < 0:
            raise ValueError("time (k) must be positive")
        if l >= k:
            raise ValueError("loop may not start after the problem bound hence l<k")
        return Be(_bmc.Bmc_Model_GetFairness(self._fsm._ptr, k, l), self._fsm.encoding.manager)
    
    
    ###########################################################################
    # This is specific to the verification of INVARSPEC (so not LTL)
    ###########################################################################
    
    def invar_dual_forward_unrolling(self, invarspec, i):
        """
        Performs one step in the unrolling of the invarspec property.
        
        In terms of pseudo code, this corresponds to::
        
            if i == 0 : 
                return Invar[0]
            else
                return Trans[i-1] & Invar[i] & Property[i-1]
        
        .. note:: 
            
            this is specific to the INVARSPEC verification
            
        :param invarspec: a booleanized, NNF formula representing an invariant 
            property.
        :param i: the time step for which the unrolling is generated.
        :return: Trans[i-1] & Invar[i] & Property[i-1]
        :raise ValueError: in case the given parameters are incorrect.
        """
        if invarspec is None:
            raise ValueError("an invarspec is expected")
        if i < 0:
            raise ValueError("Time must be a non negative integer")
        
        return Be(_bmc.Bmc_Model_Invar_Dual_forward_unrolling(
                                                    self._fsm._ptr,
                                                    invarspec._ptr,
                                                    i), self._fsm.encoding.manager) class StdioFile:
    """
    Wrapper class that provides a context manager to access a FILE* whenever the
    lower interface needs one. This makes for a more pythonic way to interact
    with APIs that need a standard file handle without having to deal with the
    low level open/close instructions. Example::

        # opens an arbitrary file of your choice.
        with StdioFile.for_name('my_output_file', 'w') as f:
            lower_interface_do_something_smart(f)

    This wrapper also gives you access to stdin, stdout and stderr which, are
    never closed despite the fact that they may be used with a `with` statement::

        # stdio is ALREADY open at this time
        with StdioFile.stdout() as out:
            lower_interface_do_something_smart(out)
        # stdio is STILL open here
    """

    def __init__(self, fname, mode):
        """
        Creates a new instance that will open the file `fname` in the `mode`
        mode.

        :param fname: the name of the file to be opened (more info -> stdio.h)
        :param  mode: the mode in which to open the file (more info -> stdio.h)
        """
        self._fname     = fname
        self._mode      = mode
        self._ptr       = None
        self._isspecial = False

    @staticmethod
    def for_name(fname=None, mode='w'):
        """
        This function acts like a generic factory that either return a handle
        for standard file if the name is specified or to stdin or stdout if the
        name is not specified (it depends on the mode)

        :return: a stdiofile for the given name or stdin/stdout if no name is
                 specified depending on the value of the mode
        """
        if fname is None:
            return StdioFile.stdin() if mode == "r" else StdioFile.stdout()
        else:
            return StdioFile(fname, mode)

    @staticmethod
    def stdin():
        """Standard input"""
        ret = StdioFile("(standard input)", "r")
        ret._ptr = utils.stdio_stdin()
        ret._isspecial = True
        return ret

    @staticmethod
    def stdout():
        """standard output"""
        ret = StdioFile("(standard output)", "w")
        ret._ptr = utils.stdio_stdout()
        ret._isspecial = True
        return ret

    @staticmethod
    def stderr():
        """standard error"""
        ret = StdioFile("(standard error)", "w")
        ret._ptr = utils.stdio_stderr()
        ret._isspecial = True
        return ret


    def __enter__(self):
        """Opens the file"""
        if not self._isspecial:
            self._ptr = utils.stdio_fopen(self._fname, self._mode)
        return self

    def __exit__(self, exception_type, exception_value, trace):
        """Makes sure the file is closed"""
        if self._ptr is not None and not self._isspecial:
            utils.stdio_fclose(self._ptr)
            self._ptr = None

    @property
    def handle(self):
        """:return: a FILE* handle to the opened stream"""
        return self._ptr
import unittest
from tests import utils as tests
from pynusmv.init          import init_nusmv, deinit_nusmv
from pynusmv.glob          import load_from_file
from pynusmv.bmc.glob      import go_bmc, bmc_exit
from pynusmv.be.expression import Be
from pynusmv.be.fsm        import BeFsm
from pynusmv.bmc.utils     import BmcModel
from pynusmv.utils         import StdioFile 



 
class TestBmcModel(unittest.TestCase):
    
    def setUp(self):
        init_nusmv()
        load_from_file(tests.current_directory(__file__)+"/models/flipflops_trans_invar_fairness.smv")
        go_bmc()
        
        self.fsm = BeFsm.global_master_instance()
        self.enc = self.fsm.encoding

    def tearDown(self):
        bmc_exit()
        deinit_nusmv()

    def test_init(self):
        # time must be >= 0
        with self.assertRaises(ValueError):
            BmcModel(self.fsm).init[-1]
        
        # at time  0
        init0 = self.fsm.encoding.shift_to_time(self.fsm.init, 0)
        self.assertEqual(init0, BmcModel(self.fsm).init[0])
        
        # at time  4
        init4 = self.fsm.encoding.shift_to_time(self.fsm.init, 4)
        self.assertEqual(init4, BmcModel(self.fsm).init[4])
        
    def test_invar(self):
        # time must be >= 0
        with self.assertRaises(ValueError):
            BmcModel(self.fsm).invar[-1]
        
        # at time  0
        invar0 = self.fsm.encoding.shift_to_time(self.fsm.invariants, 0)
        self.assertEqual(invar0, BmcModel(self.fsm).invar[0])
        
        # at time  4
        invar4 = self.fsm.encoding.shift_to_time(self.fsm.invariants, 4)
        self.assertEqual(invar4, BmcModel(self.fsm).invar[4])
    
    def test_trans(self):
        # time must be >= 0
        with self.assertRaises(ValueError):
            BmcModel(self.fsm).trans[-1]
        
        # at time  0
        trans = self.fsm.encoding.shift_to_time(self.fsm.trans, 0)
        self.assertEqual(trans, BmcModel(self.fsm).trans[0])
        
        # at time  4
        trans = self.fsm.encoding.shift_to_time(self.fsm.trans, 4)
        self.assertEqual(trans, BmcModel(self.fsm).trans[4])
    
    def test_unrolling(self):
        # comparing the clauses list is not feasible because of the formula
        # literal which is embedded in the clauses and vary from one implem
        # to the next
        # Thus, this test lets us at least get some confidence about the 
        # equivalence between the two Be's
        
        def manual_unrolling(j, bound):
            trans  = Be.true(self.enc.manager)
            for k in range(j, bound):    
                trans = trans & self.enc.shift_to_time(self.fsm.trans, k)
            return trans
       
        model      = BmcModel(self.fsm)
        manual     = manual_unrolling(4, 5)
        tested     = model.unrolling(4,5)
        # because the var ordering does not matter at all
        manual_set = set(manual.to_cnf().vars_list)
        tested_set = set(tested.to_cnf().vars_list)
        
        self.assertEqual(manual_set, tested_set)
        
    def test_unrolling_fragment(self):
        # comparing the clauses list is not feasible because of the formula
        # literal which is embedded in the clauses and vary from one implem
        # to the next
        # Thus, this test lets us at least get some confidence about the 
        # equivalence between the two Be's
        
        model      = BmcModel(self.fsm)
        # time index starts at 1
        with self.assertRaises(ValueError):
            model.unrolling_fragment[-1]
        
        self.assertEquals(model.init[0], model.unrolling_fragment[0])
        
        manual = model.invar[1] & model.trans[1] & model.invar[2]    
        tested = model.unrolling_fragment[2]
        
        # because the var ordering does not matter at all
        manual_set = set(manual.to_cnf().vars_list)
        tested_set = set(tested.to_cnf().vars_list)
        self.assertEqual(manual_set, tested_set)

    def test_path(self):
        model      = BmcModel(self.fsm)
        noinit     = model.path(3, with_init=False)
        w_init     = model.path(3)
        
        self.assertEqual(w_init, (noinit & model.init[0]))
        self.assertEqual(noinit, model.unrolling(0, 3))
        
    def test_fairness(self):
        model = BmcModel(self.fsm)
        # K, L must be consistent with one another
        with self.assertRaises(ValueError):
            model.fairness(0, 1)
             
        self.assertIsNotNone(model.fairness(3, 0))

        self.assertEqual(Be, type(model.fairness(3, 0)))