// ----------------------------------------------------------------------------
// SAT solving by the DPLL Algorithm
// ----------------------------------------------------------------------------

// the number of literals
val n: ℕ; // e.g. 3;

// the raw types
type Literal = ℤ[-n,n];
type Clause  = Set[Literal];
type Formula = Set[Clause];
type Valuation  = Set[Literal];

// a consistency condition
pred consistent(l:Literal,c:Clause) ⇔ ¬(l∈c ∧ -l∈c);

// the type restrictions
pred literal(l:Literal) ⇔ l≠0;
pred clause(c:Clause) ⇔ ∀l∈c. literal(l) ∧ consistent(l,c);
pred formula(f:Formula) ⇔ ∀c∈f. clause(c);
pred valuation(v:Valuation) ⇔ clause(v);

// the satisfaction relation
pred satisfies(v:Valuation, l:Literal) ⇔ l∈v;
pred satisfies(v:Valuation, c:Clause) ⇔ ∃l∈c. satisfies(v, l);
pred satisfies(v:Valuation, f:Formula) ⇔ ∀c∈f. satisfies(v,c);

// the satisfiability and the validity of a formula
pred satisfiable(f:Formula) ⇔ 
  ∃v:Valuation. valuation(v) ∧ satisfies(v,f);
pred valid(f:Formula) ⇔ 
  ∀v:Valuation. valuation(v) ⇒ satisfies(v,f);

// the negation of a formula
fun not(f: Formula):Formula =
  { c | c:Clause with clause(c) ∧ ∀d∈f. ∃l∈d. -l∈c };
theorem notIsFormula(f:Formula)
  requires formula(f);
⇔ formula(not(f));
theorem validIsNotSatNeg(f:Formula) 
  requires formula(f); 
⇔ valid(f) ⇔ ¬satisfiable(not(f));

// the literals of a formula
fun literals(f:Formula):Set[Literal] = 
  {l | l:Literal with ∃c∈f. l∈c};

// the result of setting a literal l in formula f to true
fun substitute(f:Formula,l:Literal):Formula = 
  {c\{-l} | c∈f with ¬(l∈c)};

// the recursive DPLL algorithm (without optimizations)
multiple pred DPLL(f:Formula)
  requires formula(f);
  ensures result ⇔ satisfiable(f);
  decreases |literals(f)|;
⇔
  if f = ∅[Clause] then
    ⊤
  else if ∅[Literal] ∈ f then
    ⊥
  else
    choose l∈literals(f) in
    DPLL(substitute(f,l)) ∨ DPLL(substitute(f,-l));

// ----------------------------------------------------------------------------
// speedup checking by computing all well-formed formulas
// ----------------------------------------------------------------------------

// compute the set of all formulas derived from f by adding
// literal +/-m in all possible ways to f
multiple fun formulas(f:Formula, m:ℕ[n]): Set[Formula] = 
  if f = ∅[Clause] then
    {f,{{m}},{{-m}}}
  else
    choose c∈f in
    let F = formulas (f\{c}, m) in
    { f∪{c} | f ∈ F } ∪
    { f∪{c∪{m}} | f ∈ F } ∪
    { f∪{c∪{-m}} | f ∈ F };

// compute the set of all well-formed formulas in m variables
multiple fun formulas(m:ℕ[n]): Set[Formula] 
  decreases m;
  ensures ∀f∈result. formula(f);
=
  if m = 0 then
    { ∅[Clause], { ∅[Literal] } }
  else let F = formulas(m-1) in
    ⋃ { formulas(f,m) | f ∈ F };   

// check with all well-formed formulas in n variables
proc main(): ()
{
  val F = formulas(n);
  check notIsFormula with F;
  check validIsNotSatNeg with F;
  check DPLL with F;
}

// -----------------------------------------------------------------
// end of file
// -----------------------------------------------------------------


