"""
Abstract SAT Solver.

All SAT solvers must inherit from this class.

.. note::

    Our SAT solver interfaces are 1-based, i.e., literals start at
    1. This is consistent with the popular DIMACS format for SAT
    solving but not with Pythion's 0-based convention. However, this
    also allows to construct clauses using simple integers.

AUTHORS:

- Martin Albrecht (2012): first version
"""

cdef class SatSolver:
    def __cinit__(self, *args, **kwds):
        """
        Constuct a new SATSolver.

        EXAMPLE::

            sage: from sage.sat.solvers.satsolver import SatSolver
            sage: solver = SatSolver()
        """
        pass

    def var(self, decision=None):
        """
        Return a *new* variable.

        INPUT:

        - ``decision`` - is this variable a deicison variable?

        EXAMPLE::

            sage: from sage.sat.solvers.satsolver import SatSolver
            sage: solver = SatSolver()
            sage: solver.var()
            Traceback (most recent call last):
            ...
            NotImplementedError
        """
        raise NotImplementedError

    def nvars(self):
        """
        Return the number of variables.

        EXAMPLE::

            sage: from sage.sat.solvers.satsolver import SatSolver
            sage: solver = SatSolver()
            sage: solver.nvars()
            Traceback (most recent call last):
            ...
            NotImplementedError
        """
        raise NotImplementedError

    def add_clause(self, lits):
        """
        Add a new clause to set of clauses.

        INPUT:

        - ``lits`` - a tuple of integers != 0

        .. note::

            If any element ``e`` in ``lits`` has ``abs(e)`` greater
            than the number of variables generated so far, then new
            variables are created automatically.

        EXAMPLE::

            sage: from sage.sat.solvers.satsolver import SatSolver
            sage: solver = SatSolver()
            sage: solver.add_clause( (1, -2 , 3) )
            Traceback (most recent call last):
            ...
            NotImplementedError
        """
        raise NotImplementedError

    def __call__(self, assumptions=None):
        """
        Solve this instance.

        INPUT:

        - ``assumptions`` - assumed variable assignments (default: ``None``)

        OUTPUT:

        - If this instance is SAT: A tuple of length ``nvars()+1``
          where the ``i``-th entry holds an assignment for the
          ``i``-th variables (the ``0``-th entry is always ``None``).

        - If this instance is UNSAT: ``False``

        - If the solver was interrupted before deciding satisfiability
          ``None``.

        EXAMPLE::

            sage: from sage.sat.solvers.satsolver import SatSolver
            sage: solver = SatSolver()
            sage: solver()
            Traceback (most recent call last):
            ...
            NotImplementedError
        """
        raise NotImplementedError

    def conflict_clause(self):
        """
        Return conflict clause if this instance is UNSAT and the last
        call used assumptions.

        EXAMPLE::

            sage: from sage.sat.solvers.satsolver import SatSolver
            sage: solver = SatSolver()
            sage: solver.conflict_clause()
            Traceback (most recent call last):
            ...
            NotImplementedError
        """
        raise NotImplementedError

    def learnt_clauses(self, unitary_only=False):
        """
        Return learnt clauses.

        INPUT:

        - ``unitary_only`` - return only unitary learnt clauses (default: ``False``)

        EXAMPLE::

            sage: from sage.sat.solvers.satsolver import SatSolver
            sage: solver = SatSolver()
            sage: solver.learnt_clauses()
            Traceback (most recent call last):
            ...
            NotImplementedError

            sage: solver.learnt_clauses(unitary_only=True)
            Traceback (most recent call last):
            ...
            NotImplementedError
        """
        raise NotImplementedError

    def __repr__(self):
        """
        TESTS::

        sage: from sage.sat.solvers.satsolver import SatSolver
        sage: solver = SatSolver()
        sage: solver
        a generic SAT solver (don't use me, inherit from me)
        """
        return "a generic SAT solver (don't use me, inherit from me)"

    def clauses(self, filename=None):
        """
        Return original clauses.

        INPUT:

        - ``filename'' - if not ``None`` clauses are written to ``filename`` in
          DIMACS format (default: ``None``)

        OUTPUT:

            If ``filename`` is ``None`` then a list of ``lits, is_xor, rhs``
            tuples is returned, where ``lits`` is a tuple of literals,
            ``is_xor`` is always ``False`` and ``rhs`` is always ``None``.

            If ``filename`` points to a writable file, then the list of original
            clauses is written to that file in DIMACS format.


        EXAMPLE::

            sage: from sage.sat.solvers.satsolver import SatSolver
            sage: solver = SatSolver()
            sage: solver.clauses()
            Traceback (most recent call last):
            ...
            NotImplementedError
        """
        raise NotImplementedError

    def __getattr__(self, name):
        """
        EXAMPLE::

            sage: from sage.sat.solvers.satsolver import SatSolver
            sage: solver = SatSolver()
            sage: solver.gens() # __getattr__ points this to clauses
            Traceback (most recent call last):
            ...
            NotImplementedError
        """
        if name == "gens":
            return self.clauses
        else:
            raise AttributeError("'%s' object has no attribute '%s'"%(self.__class__.__name__,name))

    def trait_names(self):
        """
        Allow alias to appear in tab completion.

        EXAMPLE::

            sage: from sage.sat.solvers.satsolver import SatSolver
            sage: solver = SatSolver()
            sage: solver.trait_names()
            ['gens']
        """
        return ["gens"]

