/*++
Copyright (c) 2011 Microsoft Corporation

Module Name:

    sat_watched.h

Abstract:

    Element of the SAT solver watchlist.

Author:

    Leonardo de Moura (leonardo) 2011-05-21.

Revision History:

--*/
#pragma once

#include "sat/sat_types.h"
#include "util/vector.h"

namespace sat {
    /**
       A watched element can be:
       
       1) A literal:               for watched binary clauses
       2) A pair of literals:      for watched ternary clauses
       3) A pair (literal, clause-offset): for watched clauses, where the first element of the pair is a literal of the clause.
       4) A external constraint-idx: for external constraints.

       For binary clauses: we use a bit to store whether the binary clause was learned or not.
       
       Remark: there are no clause objects for binary clauses.
    */

    class extension;

    class watched {
    public:
        enum kind {
            BINARY = 0, TERNARY, CLAUSE, EXT_CONSTRAINT
        };
    private:
        size_t   m_val1;
        unsigned m_val2; 
    public:
        watched(literal l, bool learned):
            m_val1(l.to_uint()),
            m_val2(static_cast<unsigned>(BINARY) + (static_cast<unsigned>(learned) << 2)) {
            SASSERT(is_binary_clause());
            SASSERT(get_literal() == l);
            SASSERT(is_learned() == learned);
            SASSERT(learned || is_binary_non_learned_clause());
        }

        watched(literal l1, literal l2) {
            SASSERT(l1 != l2);
            if (l1.index() > l2.index())
                std::swap(l1, l2);
            m_val1 = l1.to_uint();
            m_val2 = static_cast<unsigned>(TERNARY) + (l2.to_uint() << 2);
            SASSERT(is_ternary_clause());
            SASSERT(get_literal1() == l1);
            SASSERT(get_literal2() == l2);
        }

        unsigned val2() const { return m_val2; }

        watched(literal blocked_lit, clause_offset cls_off):
            m_val1(cls_off), 
            m_val2(static_cast<unsigned>(CLAUSE) + (blocked_lit.to_uint() << 2)) {
            SASSERT(is_clause());
            SASSERT(get_blocked_literal() == blocked_lit);
            SASSERT(get_clause_offset() == cls_off);
        }

        explicit watched(ext_constraint_idx cnstr_idx):
            m_val1(cnstr_idx),
            m_val2(static_cast<unsigned>(EXT_CONSTRAINT)) {
            SASSERT(is_ext_constraint());
            SASSERT(get_ext_constraint_idx() == cnstr_idx);
        }

        kind get_kind() const { return static_cast<kind>(m_val2 & 3); }
       
        bool is_binary_clause() const { return get_kind() == BINARY; }
        literal get_literal() const { SASSERT(is_binary_clause()); return to_literal(static_cast<unsigned>(m_val1)); }
        void set_literal(literal l) { SASSERT(is_binary_clause()); m_val1 = l.to_uint(); }
        bool is_learned() const { SASSERT(is_binary_clause()); return ((m_val2 >> 2) & 1) == 1; }

        bool is_binary_learned_clause() const { return is_binary_clause() && is_learned(); }
        bool is_binary_non_learned_clause() const { return is_binary_clause() && !is_learned(); }

        void set_learned(bool l) { if (l) m_val2 |= 4u; else m_val2 &= ~4u; SASSERT(is_learned() == l); }
                
        bool is_ternary_clause() const { return get_kind() == TERNARY; }
        literal get_literal1() const { SASSERT(is_ternary_clause()); return to_literal(static_cast<unsigned>(m_val1)); }
        literal get_literal2() const { SASSERT(is_ternary_clause()); return to_literal(m_val2 >> 2); }

        bool is_clause() const { return get_kind() == CLAUSE; }
        clause_offset get_clause_offset() const { SASSERT(is_clause()); return static_cast<clause_offset>(m_val1); }
        literal get_blocked_literal() const { SASSERT(is_clause()); return to_literal(m_val2 >> 2); }
        void set_clause_offset(clause_offset c) { SASSERT(is_clause()); m_val1 = c; }
        void set_blocked_literal(literal l) { SASSERT(is_clause()); m_val2 = static_cast<unsigned>(CLAUSE) + (l.to_uint() << 2); }
        void set_clause(literal blocked_lit, clause_offset cls_off) {
            m_val1 = cls_off;
            m_val2 = static_cast<unsigned>(CLAUSE) + (blocked_lit.to_uint() << 2);
        }

        bool is_ext_constraint() const { return get_kind() == EXT_CONSTRAINT; }
        ext_constraint_idx get_ext_constraint_idx() const { SASSERT(is_ext_constraint()); return m_val1; }
        
        bool operator==(watched const & w) const { return m_val1 == w.m_val1 && m_val2 == w.m_val2; }
        bool operator!=(watched const & w) const { return !operator==(w); }
    };

    static_assert(0 <= watched::BINARY && watched::BINARY <= 3, "");
    static_assert(0 <= watched::TERNARY && watched::TERNARY <= 3, "");
    static_assert(0 <= watched::CLAUSE && watched::CLAUSE <= 3, "");
    static_assert(0 <= watched::EXT_CONSTRAINT && watched::EXT_CONSTRAINT <= 3, "");

    struct watched_lt {
        bool operator()(watched const & w1, watched const & w2) const {
            if (w2.is_binary_clause()) return false;
            if (w1.is_binary_clause()) return true;
            if (w2.is_ternary_clause()) return false;
            if (w1.is_ternary_clause()) return true;
            return false;
        }
    };

    typedef vector<watched> watch_list;

    watched* find_binary_watch(watch_list & wlist, literal l);
    watched const* find_binary_watch(watch_list const & wlist, literal l);
    bool erase_clause_watch(watch_list & wlist, clause_offset c);
    void erase_ternary_watch(watch_list & wlist, literal l1, literal l2);
    void set_ternary_learned(watch_list& wlist, literal l1, literal l2, bool learned);

    class clause_allocator;
    std::ostream& display_watch_list(std::ostream & out, clause_allocator const & ca, watch_list const & wlist, extension* ext);

    void conflict_cleanup(watch_list::iterator it, watch_list::iterator it2, watch_list& wlist);
};

