// -----------------------------------------------------------------
// The N-queens problem
// -----------------------------------------------------------------

// the size of the board
val N:ℕ;
axiom notZero ⇔ N ≠ 0;

type Num = ℕ[N];
type Index = ℕ[N-1];
type Queens = Array[N,Index];

// do queens at (i1,j1) and (i2,j2) beat each other?
pred beats(i1:Index, j1:Index, i2:Index, j2:Index) 
⇔
  i1 = i2 ∨ j1 = j2 ∨
  i1-j1 = i2-j2 ∨
  i1+j1 = i2+j2;
   
// some of the first q queens beat each other
pred beats(n:Num, q:Queens) ⇔
  (∃k1:Index, k2:Index with k1 < k2 ∧ k2 < n. 
    beats(k1, q[k1], k2, q[k2]));
 
// the first i elements of q are a partial solution 
// to the n-queens problem 
pred queens(i:Num, n:Num, q:Queens) ⇔
  (∀k:Index with k < i. q[k] < n) ∧
  (∀k:Index with i ≤ k. q[k] = 0) ∧
  ¬beats(i, q);
  
// q is a solution to the n-queens problem 
pred queens(n:Num, q:Queens) ⇔ queens(n, n, q);
 
// a necessary and sufficient condition for
// the existence of a solution to the n-queens problem 
pred queensExist(n:Num) ⇔ n ≠ 0 ∧ n ≠ 2 ∧ n ≠ 3;
theorem queensExistValid(n:Num) ⇔ queensExist(n) ⇔ ∃q:Queens. queens(n,q);

// an arbitrary solution to the n/N-queen problem
fun queens(n:Num):Queens 
  requires queensExist(n);
= choose q:Queens with queens(n, q);
fun queens(): Queens 
  requires queensExist(N);
= queens(N);

// all solutions to the n-queen respectively N-queens problem
fun queensAll(n:Num): Set[Queens] =
  { q | q:Queens with queens(n, q) };
fun queensAll(): Set[Queens] = queensAll(N);

// queen at (i,j) does not beat any of the first n queens in q
pred beats(i:Index, j:Index, n:Num, q:Queens) ⇔
  ∃k:Index with k < n. beats(i, j, k, q[k]);
  
// the set of all solutions to the n-queens problem derived from
// the partial solution q for the first i rows
proc queensProc(i:Num, n:Num, q:Queens):Set[Queens]
  requires i ≤ n ∧ queens(i, n, q);
  ensures ∀q∈result. queens(n, q);
{
  var result:Set[Queens];
  if i = n then
    result ≔ {q};
  else
  {
    result ≔ ∅[Queens];
    for j:Index with j < n do
    {
      if ¬beats(i, j, i, q) then
      {
        val r = queensProc(i+1, n, q with [i] = j);
        result ≔ result ∪ r;
      }
    }
  }
  return result;
}

// all solutions to the n/N-queen problem
fun queensProc(n:Num): Set[Queens] 
  ensures result = queensAll(n);
= if n = 0 then ∅[Queens] else queensProc(0,n,Array[N,Index](0));
fun queensProc(): Set[Queens] 
  ensures result = queensAll(N);
= queensProc(N);

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




