Wiki Agenda Contact English version

The N-queens problem, in C with Caduceus tool

Number of solutions to the N-queens problem, C/Caduceus version


Auteurs: Jean-Christophe Filliâtre

Catégories: Bitwise operations / Tricky termination

Outils: Caduceus

Voir aussi: The N-queens problem, in Why3

see also the index (by topic, by tool, by reference, by year)


The following program computes the number of solutions to the n-queens problem
/**************************************************************************/
/*                                                                        */
/*  The Why platform for program certification                            */
/*                                                                        */
/*  Copyright (C) 2002-2010                                               */
/*                                                                        */
/*    Jean-Christophe FILLIATRE, CNRS                                     */
/*    Claude MARCHE, INRIA & Univ. Paris-sud 11                           */
/*    Yannick MOY, Univ. Paris-sud 11                                     */
/*    Romain BARDOU, Univ. Paris-sud 11                                   */
/*    Thierry HUBERT, Univ. Paris-sud 11                                  */
/*                                                                        */
/*  Secondary contributors:                                               */
/*                                                                        */
/*    Nicolas ROUSSET, Univ. Paris-sud 11 (on Jessie & Krakatoa)          */
/*    Ali AYAD, CNRS & CEA Saclay         (floating-point support)        */
/*    Sylvie BOLDO, INRIA                 (floating-point support)        */
/*    Jean-Francois COUCHOT, INRIA        (sort encodings, hyps pruning)  */
/*    Mehdi DOGGUY, Univ. Paris-sud 11    (Why GUI)                       */
/*                                                                        */
/*  This software is free software; you can redistribute it and/or        */
/*  modify it under the terms of the GNU Lesser General Public            */
/*  License version 2.1, with the special exception on linking            */
/*  described in file LICENSE.                                            */
/*                                                                        */
/*  This software is distributed in the hope that it will be useful,      */
/*  but WITHOUT ANY WARRANTY; without even the implied warranty of        */
/*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                  */
/*                                                                        */
/**************************************************************************/

/* Verification of the following 2 lines code for the N queens:

t(a,b,c){int d=0,e=a&~b&~c,f=1;if(a)for(f=0;d=(e-=d)&-e;f+=t(a-d,(b+d)*2,(
c+d)/2));return f;}main(q){scanf("%d",&q);printf("%d\n",t(~(~0<<q),0,0));}
*/

/****** abstract sets of integers *******************************************/

//@ type iset

//@ predicate in_(int x, iset s)

//@ predicate included(iset a, iset b) { \forall int i; in_(i,a) => in_(i,b) }

//@ logic int card(iset s)
//@ axiom card_nonneg : \forall iset s; card(s) >= 0

//@ logic iset empty()
//@ axiom empty_def : \forall int i; !in_(i,empty())
//@ axiom empty_card : \forall iset s; card(s)==0 <=> s==empty()

//@ logic iset diff(iset a, iset b)
/*@ axiom diff_def : 
  @   \forall iset a, iset b; \forall int i;
  @     in_(i,diff(a,b)) <=> (in_(i,a) && !in_(i,b))
  @*/

//@ logic iset add(int x, iset a)
/*@ axiom add_def : 
  @   \forall iset s; \forall int x; \forall int i;
  @     in_(i,add(x,s))  <=> (i==x || in_(i,s))
  @*/

//@ logic iset remove(int x, iset s)
/*@ axiom remove_def : 
  @   \forall iset s; \forall int x; \forall int i;
  @     in_(i,remove(x,s))  <=> (in_(i,s) && i!=x)
  @*/

/*@ axiom remove_card : 
  @   \forall iset s; \forall int i;
  @     in_(i,s) => card(remove(i,s)) == card(s) - 1
  @*/

//@ logic int min_elt(iset s)
/*@ axiom min_elt_def : 
  @   \forall iset s; card(s) > 0 => 
  @     (in_(min_elt(s), s) &&
  @     \forall int i; in_(i,s) => min_elt(s) <= i)
  @*/

//@ logic iset singleton(int x)
/*@ axiom singleton_def : \forall int i, int j; in_(j,singleton(i)) <=> j==i
  @*/

//@ logic iset succ(iset s)
/*@ axiom succ_def_1 :  
  @   \forall iset s; \forall int i; in_(i,s) => in_(i+1,succ(s))
  @*/
/*@ axiom succ_def_2 :  
  @   \forall iset s; \forall int i; in_(i,succ(s)) => i>=1 && in_(i-1,s)
  @*/

//@ logic iset pred(iset s)
/*@ axiom pred_def_1 : 
  @   \forall iset s; \forall int i; i>=1 => in_(i,s) => in_(i-1,pred(s))
  @*/
/*@ axiom pred_def_2 : 
  @   \forall iset s; \forall int i; in_(i,pred(s)) => in_(i+1,s)
  @*/

//@ logic iset below(int n)
//@ axiom below_def : \forall int n, int i; in_(i, below(n)) <=> 0<=i<n
//@ axiom below_card : \forall int n; card(below(n)) == n

/****** interpretation of C ints as abstract sets of integers ***************/

//@ logic iset iset(int x)

//@ axiom iset_c_zero : \forall int x; iset(x)==empty() <=> x==0

/*@ axiom iset_c_remove : 
  @  \forall int x, int a, int b; 
  @    iset(b)==singleton(x) => in_(x,iset(a)) => iset(a-b)==remove(x, iset(a))
  @*/

// lowest bit trick
/*@ axiom iset_c_min_elt :
  @   \forall int x; x != 0 => iset(x&-x) == singleton(min_elt(iset(x)))
  @*/
/*@ axiom iset_c_min_elt_help :
  @   \forall int x; x != 0 <=> x&-x != 0
  @*/

/*@ axiom iset_c_diff :
  @  \forall int a, int b; iset(a&~b) == diff(iset(a), iset(b))
  @*/

/*@ axiom iset_c_add :
  @  \forall int x, int a, int b; 
  @    iset(b)==singleton(x) => !in_(x,iset(a)) => iset(a+b) == add(x, iset(a))
  @*/

// @ axiom iset_c_succ : \forall int a; iset(a*2) == succ(iset(a))

// @ axiom iset_c_pred : \forall int a; iset(a/2) == pred(iset(a))

//@ axiom iset_c_below : \forall int n; iset(~(~0<<n)) == below(n)

/****** helper lemmas *******************************************************/

/* @ axiom included_trans : \forall iset a, iset b, iset c;
  @   included(a,b) => included(b,c) => included(a,c)
  @*/

/* @ axiom included_diff : \forall iset a, iset b; included(diff(a,b), a) */

/* @ axiom included_remove : \forall iset a, int x; included(remove(x,a), a) */

/***************************************************************************/

// t1: termination of the for loop
int t1(int a, int b, int c){
  int d, e=a&~b&~c, f=1;
  if (a)
    /*@ variant card(iset(e)) */
    for (f=0; d=e&-e; e-=d) {
      f+=t1(a-d,(b+d)*2,(c+d)/2);
    }
  return f;
}

/****************************************************************************/

// t2: termination of the recursive function: card(iset(a)) decreases
int t2(int a, int b, int c){
  int d, e=a&~b&~c, f=1;
  //@ label L
  if (a)
    /*@ invariant included(iset(e),\at(iset(e),L)) */
    for (f=0; d=e&-e; e-=d) {
      //@ assert \exists int x; iset(d) == singleton(x) && in_(x,iset(e)) 
      //@ assert card(iset(a-d)) < card(iset(a))
      f+=t2(a-d,(b+d)*2,(c+d)/2);
    }
  return f;
}

/****************************************************************************/

//@ logic int N() // N queens on a NxN chessboard 
//@ axiom N_positive : N() > 0

// t and u have the same prefix [0..i[
/*@ predicate eq_prefix(int *t, int *u, int i) {
  @   \forall int k; 0 <= k < i => t[k]==u[k]
  @ } 
  @*/

//@ predicate eq_sol(int *t, int *u) { eq_prefix(t, u, N()) } 

int** sol; // sol[i] is the i-th solution
/*@ axiom dont_bother_me_I_am_a_ghost_1 : 
  @   \forall int i; \valid(sol+i) */
/*@ axiom dont_bother_me_I_am_a_ghost_2 : 
  @   \forall int i, int j; \valid(sol[i]+j) */

int s = 0; // next empty slot in sol for a solution

int* col; // current solution being built
int k;    // current row in the current solution

// s stores a partial solution, for the rows 0..k-1
/*@ predicate partial_solution(int k, int* s) {
  @   \forall int i; 0 <= i < k => 
  @     0 <= s[i] < N() &&
  @     (\forall int j; 0 <= j < i => s[i] != s[j] &&
  @                                   s[i]-s[j] != i-j &&
  @                                   s[i]-s[j] != j-i)
  @ }
  @*/

//@ predicate solution(int* s) { partial_solution(N(), s) }

// lemma
/*@ axiom partial_solution_eq_prefix:
  @   \forall int *t, int *u; \forall int k;
  @     partial_solution(k,t) => eq_prefix(t,u,k) => partial_solution(k,u)
  @*/

/*@ predicate lt_sol(int *s1, int *s2) {
  @   \exists int i; 0 <= i < N() && eq_prefix(s1, s2, i) && s1[i] < s2[i]
  @ } 
  @*/

/* s[a..b[ is sorted for lt_sol */
/*@ predicate sorted(int **s, int a, int b) {
  @   \forall int i, int j; a <= i < j < b => lt_sol(s[i], s[j])
  @ } 
  @*/

/*@ requires x != 0
  @ ensures  \result == min_elt(iset(x))
  @*/
int min_elt(int x);

/*@ requires solution(col)
  @ assigns  s, sol[s][0..N()-1]
  @ ensures  s==\old(s)+1 && eq_sol(sol[\old(s)], col)
  @*/
void store_solution();

/*@ requires
  @   0 <= k && k + card(iset(a)) == N() && 0 <= s &&
  @   pre_a:: (\forall int i; in_(i,iset(a)) <=> 
  @            (0<=i<N() && \forall int j; 0<=j<k => i != col[j])) &&
  @   pre_b:: (\forall int i; i>=0 => (in_(i,iset(b)) <=> 
  @            (\exists int j; 0<=j<k && col[j] == i+j-k))) &&
  @   pre_c:: (\forall int i; i>=0 => (in_(i,iset(c)) <=> 
  @            (\exists int j; 0<=j<k && col[j] == i+k-j))) &&
  @   partial_solution(k, col)
  @ assigns
  @   col[k..], s, k, sol[s..][..]
  @ ensures  
  @   \result == s - \old(s) && \result >= 0 && k == \old(k) &&
  @   sorted(sol, \old(s), s) &&
  @   \forall int* t; ((solution(t) && eq_prefix(col,t,k)) <=>
  @                    (\exists int i; \old(s)<=i<s && eq_sol(t, sol[i])))
  @*/
int t3(int a, int b, int c){
  int d, e=a&~b&~c, f=1;
  //@ label L
  if (a)
    /*@ invariant 
      @   included(iset(e),\at(iset(e),L)) &&
      @   f == s - \at(s,L) && f >= 0 && k == \old(k) && 
      @   partial_solution(k, col) &&
      @   sorted(sol, \at(s,L), s) &&
      @   \forall int *t; 
      @     (solution(t) && 
      @      \exists int di; in_(di, diff(\at(iset(e),L), iset(e))) &&
      @          eq_prefix(col,t,k) && t[k]==di) <=>
      @     (\exists int i; \at(s,L)<=i<s && eq_sol(t, sol[i]))
      @ loop_assigns
      @   col[k..], s, k, sol[s..][..]
      @*/
    for (f=0; d=e&-e; e-=d) {
      //@ assert \exists int x; iset(d) == singleton(x) && in_(x,iset(a)) 
      //@ ghost col[k] = min_elt(d);            // ghost code 
      //@ ghost k++;                            // ghost code
      f += t3(a-d, (b+d)*2, (c+d)/2);
      //@ ghost k--;                            // ghost code
    }
  //@ ghost else 
  //@ ghost   store_solution(); // ghost code
  return f;
}

/*@ requires 
  @   n == N() && s == 0 && k == 0
  @ ensures 
  @   \result == s &&
  @   sorted(sol, 0, s) &&
  @   \forall int* t; 
  @      solution(t) <=> (\exists int i; 0<=i<\result && eq_sol(t,sol[i]))
  @*/
int queens(int n) {
  return t3(~(~0<<n),0,0);
}