From eef943717e684b6713d306f59ac369edb4104387 Mon Sep 17 00:00:00 2001
From: Vasco Pedro <vp@di.uevora.pt>
Date: Mon, 4 Jul 2016 17:08:17 +0100
Subject: [PATCH] Update to PaCCS version 0.91

---
 Makefile                        |  12 +++++++-----
 README                          |  11 +++++++++--
 TUNING                          |  15 ++++++++++-----
 bench/all.c                     |   2 +-
 bench/bibd.c                    |  14 +++++++-------
 bench/c-exactly.c               |   5 +++--
 bench/change.c                  |  14 ++++----------
 bench/costas.c                  |  17 +++++------------
 bench/golfers.c                 |   2 +-
 bench/golomb.c                  |  10 +++++-----
 bench/graphs.c                  |   1 +
 bench/k-queens.c                |  16 +++++++++-------
 bench/langford.c                |   8 +++++---
 bench/magic-seq.c               |   6 ++++--
 bench/magic-square.c            |   7 ++++---
 bench/money.c                   |   2 +-
 bench/partition.c               |   5 +++--
 bench/qap.c                     |  41 +++++++++++++++++------------------------
 bench/queens.c                  |   9 +++++----
 bench/queens2.c                 |  19 +++++++++++--------
 bench/sudoku.c                  |  11 +++++++----
 bench/times.c                   |   5 ++---
 src/VERSION                     |   2 +-
 src/agents-splitgo-mpi.c        | 370 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 src/agents-splitgo.c            |  62 +++++++++++++++++++++++++++++++++-----------------------------
 src/bound.c                     |  10 +++++-----
 src/constraints.c               | 195 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------------------------------------------------------------------
 src/constraints.h               |  39 +++++++++++++++++++++------------------
 src/constraints/all-different.c |  10 +++-------
 src/constraints/element-var.c   |   7 ++-----
 src/constraints/element.c       |   7 ++-----
 src/constraints/eq.c            |   8 ++------
 src/constraints/exactly-one.c   |   7 ++-----
 src/constraints/exactly-var.c   |   7 ++-----
 src/constraints/exactly-vars.c  |   7 ++-----
 src/constraints/exactly.c       |   7 ++-----
 src/constraints/knapsack.c      |   7 ++-----
 src/constraints/knapsack2.c     | 122 ++++++++++----------------------------------------------------------------------------------------------------------------
 src/constraints/le.c            |  81 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
 src/constraints/lt.c            |  75 ++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------
 src/constraints/max.c           |   7 ++-----
 src/constraints/min.c           |   7 ++-----
 src/constraints/minus-eq.c      |   8 ++------
 src/constraints/minus-ne.c      |   8 ++------
 src/constraints/ne.c            |   8 ++------
 src/constraints/nogoods.c       |   7 ++-----
 src/constraints/plus-gt.c       |   7 ++-----
 src/constraints/poly-eq-k.c     | 329 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 src/constraints/poly-eq.c       | 532 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 src/constraints/poly-le-k.c     | 346 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 src/constraints/poly-ne-k.c     |  13 ++++++-------
 src/constraints/poly-ne.c       |  13 ++++++-------
 src/constraints/sum-prod.c      |   7 ++-----
 src/constraints/sum.c           |   7 ++-----
 src/constraints/sum2.c          |   7 ++-----
 src/constraints/var-eq-minus.c  | 148 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------------------------------------
 src/constraints/var-eq-times.c  |   7 ++-----
 src/dsearch-sg.c                |  31 +++++++++++++------------------
 src/fdc_int.h                   |  29 +++++++----------------------
 src/matching.c                  |  24 ++++++++++++++----------
 src/options.c                   |   4 ++--
 src/paccs.h                     |   3 +++
 src/packed.c                    |  10 ++++------
 src/packed.h                    |   2 +-
 src/pool.c                      | 140 +++++++++++++++++++++++++++++++++++++++++++++-----------------------------------------------------------------------------------------------
 src/problem.c                   |   8 ++++----
 src/revisions.c                 |  14 +++++++-------
 src/splitting.c                 |  10 ++++++----
 src/util.c                      |  12 +++++++-----
 src/util.h                      |   8 ++++++++
 src/values-bitmap.c             |  16 ++++++++--------
 src/values-intervals.c          |  30 +++++++++++++++---------------
 src/values.c                    |   2 ++
 src/variables.c                 |  46 ++++++++++++++++++++++++++++++++++++++++++----
 src/variables.h                 |  17 +++++++++--------
 75 files changed, 1655 insertions(+), 1467 deletions(-)
 create mode 100644 src/constraints/poly-le-k.c
 create mode 100644 src/util.h

diff --git a/Makefile b/Makefile
index 2585811..b8f006a 100644
--- a/Makefile
+++ b/Makefile
@@ -34,11 +34,11 @@ CPPFLAGS = -I$(SRC) $(ALL_DEFINES)
 
 EXTRA_DEFINES =
 
-DEFINES = -DFAST # -DNDEBUG
-DEFAULTS = -DREVISION_IS_VAR -DGROWABLE_POOL -DINDEX_IN_POOL -DSTORE_IN_POOL \
+DEFINES = # -DTRACE -DNDEBUG
+DEFAULTS = -DREVISION_IS_VAR -DGROWABLE_POOL -DSTORE_IN_POOL \
 	   -DNEW_ENTRANCE -DRANDOM_VICTIM -DDOMAIN_BOUNDS
-REQUIRED = -DDISTRIBUTED_SOLVER -DSPLITGO -DCOMPACT_DOMAINS -DCONSTRAINT_CLASS \
-	   -DUSE_STORE -DPACK_PROBLEM
+REQUIRED = -DDISTRIBUTED_SOLVER -DSPLITGO -DCOMPACT_DOMAINS -DUSE_STORE \
+	   -DPACK_PROBLEM
 
 ALL_DEFINES = $(REQUIRED) $(DEFAULTS) $(DEFINES) $(EXTRA_DEFINES)
 
@@ -119,6 +119,8 @@ list.o: $(SRC)/list.c $(SRC)/list.h
 values.o: values.h values-bitmap.c values-intervals.c fdc_int.h
 
 
+queens queens-mpi : override DEFAULTS += -DDISABLE_ENTAILED
+
 costas costas-mpi : override DEFAULTS += -DCONSTRAINT_TEMPS
 
 qap qap-mpi : override DEFAULTS += \
@@ -131,7 +133,7 @@ graphs graphs-mpi : matching.c
 
 partition partition-mpi : override DEFAULTS += -DCONSTRAINT_TEMPS
 
-golomb golomb-mpi : override DEFAULTS += -DDOMAIN_BOUNDS
+golomb golomb-mpi : override DEFAULTS += -DDOMAIN_BOUNDS -DDOMAIN_BITS=128
 
 langford langford-mpi : override DEFAULTS += \
   -UDOMAIN_BOUNDS -DINLINE_DOMAINS -DCONSTRAINT_TEMPS -DDISABLE_ENTAILED
diff --git a/README b/README
index 6ef4482..fc141fd 100644
--- a/README
+++ b/README
@@ -1,4 +1,4 @@
-PaCCS version 0.90d
+PaCCS version 0.91
 
 All files Copyright (C) 2009-2016 Vasco Pedro <vp@di.uevora.pt>
 All rights reserved.
@@ -38,7 +38,7 @@ Useful options include:
   -DDOMAIN_BOUNDS	include the domain's current minimum and maximum values
 			in its representation (DEFAULT)
 
-  -DFAST		be quiet (DEFAULT)
+  -DTRACE		show what's happening (part of it, anyway)
 
 For other options, take a look at the Makefile, at src/options.c, or
 at the full solver source.
@@ -150,6 +150,8 @@ Integer constants
 
   fd_int fd_new(int min, int max)	create a new variable with domain
 					[min, max]
+  fd_int fd_const(int value)		create a new variable with domain
+					[value, value]
   fd_label(fd_int vars[], int nvars)	set variables to be labelled (the
 					DEFAULT is to label all variables)
   fd_var_single(fd_int var, int *val)	tests whether the variable domain
@@ -181,6 +183,10 @@ Global constraints
   fd_all_different(fd_int X[], int n)
 	all n variables in X have different values
 
+  fd_fake_all_different(fd_int X[], int n)
+	all-different implemented with pairwise fd_ne() constraints
+	between the variables in X
+
   fd_element(fd_int X[], int n, fd_int y, int k)	X[y] = k
   fd_element_var(fd_int X[], int n, fd_int y, fd_int z)	X[y] = z
 
@@ -191,6 +197,7 @@ Global constraints
 
   fd_poly_eq(int C[], fd_int Y[], int n, fd_int z)	z <= C . Y <= z
   fd_poly_eq_k(int C[], fd_int Y[], int n, int k)	C . Y = k
+  fd_poly_le_k(int C[], fd_int Y[], int n, int k)	C . Y <= k
   fd_poly_ne(int C[], fd_int Y[], int n, fd_int z)	C . Y != z
   fd_poly_ne_k(int C[], fd_int Y[], int n, int k)	C . Y != k
   fd_knapsack2(fd_int X[], fd_int Y[], int n, fd_int z)	z <= X . Y <= z
diff --git a/TUNING b/TUNING
index d7e2a52..989a7b3 100644
--- a/TUNING
+++ b/TUNING
@@ -11,11 +11,14 @@ qap
 costas
 	-DCONSTRAINT_TEMPS -DDOMAIN_BOUNDS
 	--label --most-constrained
+	(--label --first-var performs better in some cases, at least for the 1st solution)
+
 	--split-even	(first solution)
 
 	N <= DOMAIN_BITS / 2
 
 queens
+	-DDISABLE_ENTAILED
 	-DDOMAIN_BOUNDS ??? [not ism]
 	--first-fail
 
@@ -41,7 +44,7 @@ golomb
 	-DDOMAIN_BOUNDS (-DCONSTRAINT_TEMPS -DDISABLE_ENTAILED [cri-lima])
 	--label{, --most-connected, --most-constrained}
 	--split-eager
-	(--split-even is generally slower, but more regular)
+	(--split-even is generally slower, but more stable)
 
 langford
     [bicho]
@@ -59,13 +62,15 @@ langford
 	-DINLINE_DOMAINS -DCONSTRAINT_TEMPS (third best)
 	-DDOMAIN_BOUNDS -DCONSTRAINT_TEMPS -DDISABLE_ENTAILED (fourth best)
 	-DDOMAIN_BOUNDS (fifth best)
-
-	--first-fail --val-max (--count-solutions or failure)
-	--size-degree --val-max (first solution, not always but when
-				 it works, the effect is dramatic)
+    [*]
+	? -DDOMAIN_BOUNDS
 
 	--split-eager
 
+	--first-fail {,--val-max} (--count-solutions or inconsistent instances)
+	--size-degree (first solution)
+	(--first --val-max is a good overall compromise)
+
 golfers
 	-D?
 	--label --val-max?
diff --git a/bench/all.c b/bench/all.c
index b884130..e18eea5 100644
--- a/bench/all.c
+++ b/bench/all.c
@@ -28,7 +28,7 @@ int main(int argc, char *argv[])
       NV = atoi(argv[i]);
 
   if (NV > MAX_VARIABLES)
-    _fd_fatal("more than MAX_VARIABLES variables requested");
+    fd__fatal("more than MAX_VARIABLES variables requested");
 
   for (i = 0; i < NV; ++i)
     vars[i] = fd_new(0,  MV);
diff --git a/bench/bibd.c b/bench/bibd.c
index bc8d4d6..b4eb355 100644
--- a/bench/bibd.c
+++ b/bench/bibd.c
@@ -33,7 +33,7 @@ int main(int argc, char *argv[])
 #ifndef GECODE_STYLE
     else if (argc - i < 3)
       {	       
-	_fd_debug("must give V, B, and K (or nothing)\n");
+	fd__error("must give V, B, and K (or nothing)\n");
 	return 1;
       }
     else
@@ -47,7 +47,7 @@ int main(int argc, char *argv[])
 
   if (R * V != K * B)
     {
-      _fd_debug("%d * %d / %d must be an integer\n", K, B, V);
+      fd__error("%d * %d / %d must be an integer\n", K, B, V);
       return 1;
     }
 
@@ -55,13 +55,13 @@ int main(int argc, char *argv[])
 
   if (lambda * (V - 1) != R * (K - 1))
     {
-      _fd_debug("%d * (%d - 1) / (%d - 1) must be an integer\n", R, K, V);
+      fd__error("%d * (%d - 1) / (%d - 1) must be an integer\n", R, K, V);
       return 1;
     }
 #else /* GECODE_STYLE */
     else if (argc - i < 3)
       {	       
-	_fd_debug("must give V, K, and lambda (or nothing)\n");
+	fd__error("must give V, K, and lambda (or nothing)\n");
 	return 1;
       }
     else
@@ -75,7 +75,7 @@ int main(int argc, char *argv[])
 
   if (B * K * (K - 1) != lambda * V * (V - 1))
     {
-      _fd_debug("warning: %d * %d * (%d - 1) / %d / (%d - 1) must be an integer\n",
+      fd__error("warning: %d * %d * (%d - 1) / %d / (%d - 1) must be an integer\n",
 		lambda, V, V, K, K);
 //      return 1;
     }
@@ -84,12 +84,12 @@ int main(int argc, char *argv[])
 
   if (R * V != K * B)
     {
-      _fd_debug("warning: %d * %d / %d must be an integer\n", K, B, V);
+      fd__error("warning: %d * %d / %d must be an integer\n", K, B, V);
 //      return 1;
     }
 #endif /* GECODE_STYLE */
 
-  _fd_debug("V = %d, B = %d, K = %d, R = %d, lambda = %d\n", V, B, K, R, lambda);
+  fd__info("V = %d, B = %d, K = %d, R = %d, lambda = %d\n", V, B, K, R, lambda);
 
   vs = calloc(V * B, sizeof(fd_int));
   ts = calloc(V > B ? V : B, sizeof(fd_int));
diff --git a/bench/c-exactly.c b/bench/c-exactly.c
index cc6adab..33a1447 100644
--- a/bench/c-exactly.c
+++ b/bench/c-exactly.c
@@ -1,10 +1,11 @@
 #include <stdio.h>
+#include <string.h>
 
-#include "fdc_int.h"
+#include <paccs.h>
 
 #define N 3
 
-main(int argc, char *argv[])
+int main(int argc, char *argv[])
 {
   fd_int vs[N], cardinal;
   int solutions = 0, one_solution = 1;
diff --git a/bench/change.c b/bench/change.c
index dad765e..977b051 100644
--- a/bench/change.c
+++ b/bench/change.c
@@ -1,4 +1,5 @@
 #include <stdio.h>
+#include <stdlib.h>
 #include <fcntl.h>
 #include <errno.h>
 #include <ctype.h>
@@ -6,13 +7,6 @@
 
 #include "fdc_int.h"
 
-// try to save on variables
-static fd_int fd_const[MAX_VALUE + 1];
-
-#define FD_CONST(n) (fd_const[n] ? fd_const[n] : (fd_const[n] = fd_new(n, n)))
-
-//#define FD_CONST(n) fd_new(n, n)
-
 #define NCOINS (sizeof(coins_value) / sizeof(*coins_value))
 
 int coins_value[] = { 1, 2, 5, 10, 20, 50 };
@@ -33,15 +27,15 @@ int main(int argc, char *argv[])
     else if (isdigit(*argv[i]))
       am = atoi(argv[i]);
     else
-      _fd_error("%s: unknown argument `%s'\n", argv[0], argv[i]);
+      fd__error("%s: unknown argument `%s'\n", argv[0], argv[i]);
 
   for (i = 0; i < NCOINS; ++i)
-    coins[i] = FD_CONST(coins_value[i]);
+    coins[i] = fd_const(coins_value[i]);
 
   for (i = 0; i < NCOINS; ++i)
     change[i] = fd_new(0, MAX_VALUE);
 
-  amount = FD_CONST(am);
+  amount = fd_const(am);
   fd_knapsack2(change, coins, NCOINS, amount);
 
   ncoins = fd_new(0, MAX_VALUE);
diff --git a/bench/costas.c b/bench/costas.c
index b9d2960..cd0136c 100644
--- a/bench/costas.c
+++ b/bench/costas.c
@@ -3,17 +3,10 @@
 
 //#include <fcntl.h>
 //#include <errno.h>
-//#include <ctype.h>
+#include <ctype.h>
 #include <string.h>
 
-#include "fdc_int.h"
-
-// try to save on variables
-static fd_int fd_const[MAX_VALUE + 1];
-
-#define FD_CONST(n) (fd_const[n] ? fd_const[n] : (fd_const[n] = fd_new(n, n)))
-
-//#define FD_CONST(n) fd_new(n, n)
+#include <paccs.h>
 
 /*
   Costas arrays (communicated by Daniel Diaz)
@@ -57,7 +50,7 @@ int main(int argc, char *argv[])
 	break;
       }
     else
-      _fd_error("%s: unknown option `%s'\n", argv[0], argv[i]);
+      fd__error("%s: unknown option `%s'\n", argv[0], argv[i]);
 
   array = calloc(N, sizeof(*array));
 
@@ -69,7 +62,7 @@ int main(int argc, char *argv[])
       if (strchr(argv[i], '-'))
 	b = atoi(strchr(argv[i], '-') + 1);
 
-      array[j] = (a == b) ? FD_CONST(a - 1) : fd_new(a - 1, b - 1);
+      array[j] = fd_new(a - 1, b - 1);
     }
 
   for (; j < N; ++j)
@@ -91,7 +84,7 @@ int main(int argc, char *argv[])
     for (j = 0; j < N - 1 - i; ++j)
       diffs(i, j) = fd_new(0, 2 * (N - 1));
 
-  diffeq[0] = FD_CONST(N - 1);
+  diffeq[0] = fd_const(N - 1);
 
   for (k = 1; k < N - 1; ++k)
     {
diff --git a/bench/golfers.c b/bench/golfers.c
index 1d897e7..168ca59 100644
--- a/bench/golfers.c
+++ b/bench/golfers.c
@@ -34,7 +34,7 @@ int main(int argc, char *argv[])
       use_label = 1;
     else if (argc - i < 3)
       {	       
-	_fd_debug("must give G, K, and W (or nothing)\n");
+	fd__error("must give G, K, and W (or nothing)\n");
 	return 1;
       }
     else
diff --git a/bench/golomb.c b/bench/golomb.c
index 17a1506..b9ff094 100644
--- a/bench/golomb.c
+++ b/bench/golomb.c
@@ -74,17 +74,17 @@ int main(int argc, char *argv[])
   seed = time(0);
 #else
   if ((dev_random = open("/dev/urandom", O_RDONLY)) == -1)
-    _fd_fatal("/dev/urandom: %s\n", strerror(errno));
+    fd__fatal("/dev/urandom: %s\n", strerror(errno));
 
   read(dev_random, &seed, sizeof(seed));
 #endif
 
   srandom(seed);
-  _fd_debug("seed = %u\n", seed);
+  fd__trace("seed = %u\n", seed);
 #endif
 
   if (!fixed)
-    _fd_debug("length upper bound is %d\n", DOMMAX);
+    fd__trace("length upper bound is %d\n", DOMMAX);
 
   D = M * (M - 1) / 2;
 
@@ -147,7 +147,7 @@ int main(int argc, char *argv[])
   j = 0;
   for (d = 1; d < M; ++d)
     {
-      fd_int s = fd_new(d * (d + 1) / 2, d * (d + 1) / 2);
+      fd_int s = fd_const(d * (d + 1) / 2);
 
       for (i = d; i < M; ++i)
 	fd_ge(differences[j++], s);
@@ -157,7 +157,7 @@ int main(int argc, char *argv[])
   j = 0;
   for (d = 1; d < M; ++d)
     {
-      fd_int grl = fd_new(minimum_length[d + 1], minimum_length[d + 1]);
+      fd_int grl = fd_const(minimum_length[d + 1]);
 
       for (i = d; i < M; ++i)
 	fd_ge(differences[j++], grl);
diff --git a/bench/graphs.c b/bench/graphs.c
index 58b9e2e..3655b7c 100644
--- a/bench/graphs.c
+++ b/bench/graphs.c
@@ -1,6 +1,7 @@
 /* graph colouring � la Gecode */
 
 #include <stdio.h>
+#include <stdlib.h>
 #include <stdint.h>
 #include <string.h>
 
diff --git a/bench/k-queens.c b/bench/k-queens.c
index d923f27..d3192f7 100644
--- a/bench/k-queens.c
+++ b/bench/k-queens.c
@@ -1,4 +1,5 @@
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -16,8 +17,6 @@ int main(int argc, char *argv[])
   fd_int queens[NMAX];
   int solutions = 0, one_solution = 1;
   int i, j;
-  int seed;
-  int dev_random;
   fd_int copy;
   int ncopies = 4;
 
@@ -41,7 +40,7 @@ int main(int argc, char *argv[])
     }
 
   if (N > NMAX)
-    _fd_fatal("queens: more than NMAX queens requested");
+    fd__fatal("queens: more than NMAX queens requested");
 
   if (ncopies < 1)
     ncopies = 1;
@@ -49,20 +48,23 @@ int main(int argc, char *argv[])
   copy = fd_new(0, ncopies - 1);	// XXX: only allows MAX_VALUE+1 copies
 
 #ifdef LOCAL_SEARCH
+  int seed;
 #if 01
   seed = time(0);
   seed = 1206987119;
   //seed = 1207917731; // N = 6, best improvement termina
   //seed = 0;
 #else
-    if ((dev_random = open("/dev/urandom", O_RDONLY)) == -1)
-    _fd_fatal("/dev/urandom: %s\n", strerror(errno));
+  int dev_random;
+
+  if ((dev_random = open("/dev/urandom", O_RDONLY)) == -1)
+    fd__fatal("/dev/urandom: %s\n", strerror(errno));
 
   read(dev_random, &seed, sizeof(seed));
 #endif
 
   srandom(seed);
-  _fd_debug("seed = %u\n", seed);
+  fd__trace("seed = %u\n", seed);
 #endif
 
 /*
@@ -118,7 +120,7 @@ int main(int argc, char *argv[])
 	  {
 	    int j;
 
-	    _fd_error("\nsolution contains non-singleton variable\n");
+	    fd__error("\nsolution contains non-singleton variable\n");
 
 	    for (j = 0; j < N; ++j)
 	      {
diff --git a/bench/langford.c b/bench/langford.c
index 82d8898..b78a5c7 100644
--- a/bench/langford.c
+++ b/bench/langford.c
@@ -1,7 +1,9 @@
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
+#include <ctype.h>
 
-#include "fdc_int.h"
+#include <paccs.h>
 
 // Langford's number problem (CSPLib 024)
 
@@ -58,7 +60,7 @@ int main(int argc, char *argv[])
       break;
     else
       {
-        _fd_error("%s: invalid argument `%s'\n", argv[0], argv[arg]);
+        fd__error("%s: invalid argument `%s'\n", argv[0], argv[arg]);
 
         return 2;
       }
@@ -74,7 +76,7 @@ int main(int argc, char *argv[])
   //seed = 1206468701;
   //seed = 1208196137; // converges to a local minimum with MCH
   srandom(seed);
-  _fd_debug("seed = %u\n", seed);
+  fd__trace("seed = %u\n", seed);
 #endif
 
   for (i = 0; arg < argc; ++arg, ++i)
diff --git a/bench/magic-seq.c b/bench/magic-seq.c
index 1890a2a..24dae1d 100644
--- a/bench/magic-seq.c
+++ b/bench/magic-seq.c
@@ -1,6 +1,8 @@
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
-#include "fdc_int.h"
+#include <paccs.h>
 
 #define MAX_N 512
 
@@ -20,7 +22,7 @@ main(int argc, char *argv[])
       N = atoi(argv[i]);
 
   if (N > MAX_N)
-    _fd_fatal("N > MAX_N");
+    fd__fatal("N > MAX_N");
 
   for (i = 0; i < N; ++i)
     vs[i] = fd_new(0, N - 1);
diff --git a/bench/magic-square.c b/bench/magic-square.c
index 9e5e4d9..2baf020 100644
--- a/bench/magic-square.c
+++ b/bench/magic-square.c
@@ -1,9 +1,10 @@
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
 
-#include "fdc_int.h"
+#include <paccs.h>
 
 // (CSPLib 019)
 
@@ -37,7 +38,7 @@ int main(int argc, char *argv[])
       }
 
   if (N > NMAX)
-    _fd_fatal("magic-square: maximum order exceeded");
+    fd__fatal("magic-square: maximum order exceeded");
 
   j = 0;
   for (++i; i < argc; ++i)
@@ -158,7 +159,7 @@ int main(int argc, char *argv[])
       for (l = 0; l < N; ++l)
 	for (c = 0; c < N; ++c)
 	  if (!fd_var_single(square[l][c], NULL))
-	    _fd_fatal("solution contains non-singleton variable");
+	    fd__fatal("solution contains non-singleton variable");
 #endif
 
       for (l = 0; l < N; ++l)
diff --git a/bench/money.c b/bench/money.c
index 9355c77..0187ff6 100644
--- a/bench/money.c
+++ b/bench/money.c
@@ -2,7 +2,7 @@
 #include <string.h>
 #include <ctype.h>
 
-#include "fdc_int.h"
+#include <paccs.h>
 
 #define LETTERS 26
 #define MAX_SIZE (10 * LETTERS)		// whatever
diff --git a/bench/partition.c b/bench/partition.c
index 2ae1cb9..92553e1 100644
--- a/bench/partition.c
+++ b/bench/partition.c
@@ -3,8 +3,9 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
-#include "fdc_int.h"
+#include <paccs.h>
 
 int main(int argc, char *argv[])
 {
@@ -25,7 +26,7 @@ int main(int argc, char *argv[])
 
   if ((vs = malloc(2 * N * sizeof(*vs))) == NULL ||
       (sqs0 = malloc(N * sizeof(*sqs0))) == NULL)
-    _fd_fatal("could not allocate enough memory");
+    fd__fatal("could not allocate enough memory");
 
   values = 2 * N;
   sum = N * (2 * N + 1);
diff --git a/bench/qap.c b/bench/qap.c
index df0584d..efa9be7 100644
--- a/bench/qap.c
+++ b/bench/qap.c
@@ -21,13 +21,6 @@
 
 #include "fdc_int.h"
 
-// try to save on variables
-static fd_int fd_const[MAX_VALUE + 1];
-
-#define FD_CONST(n) (fd_const[n] ? fd_const[n] : (fd_const[n] = fd_new(n, n)))
-
-//#define FD_CONST(n) fd_new(n, n)
-
 #define NMAX 128
 
 typedef struct {
@@ -249,7 +242,7 @@ int main(int argc, char *argv[])
       ;
     else
       {
-	_fd_error("%s: invalid argument `%s'\n", argv[0], argv[j]);
+	fd__error("%s: invalid argument `%s'\n", argv[0], argv[j]);
 
 	return 2;
       }
@@ -260,10 +253,10 @@ int main(int argc, char *argv[])
   N = P->size;
 
   if (N > NMAX)
-    _fd_fatal("qap: problem too big (increase NMAX)");
+    fd__fatal("qap: problem too big (increase NMAX)");
 #ifdef QAP_MODEL_B
   if (N * N > DOMAIN_BITS)
-    _fd_fatal("qap: problem too big (model B) (increase DOMAIN_BITS)");
+    fd__fatal("qap: problem too big (model B) (increase DOMAIN_BITS)");
 #endif
 
   if (swap)
@@ -272,7 +265,7 @@ int main(int argc, char *argv[])
     }
 
   if (P->name)
-    _fd_debug("%s\n", P->name);
+    fd__trace("%s\n", P->name);
 
   {
     int diag_cost = 0, tb = 0;
@@ -284,7 +277,7 @@ int main(int argc, char *argv[])
       for (c = 0; c < N; ++c)
 	tb += Pflow(l, c) * Pdist(l, c);
 
-    _fd_debug("tight bound %d (diagonal %d)\n", tb, diag_cost);
+    fd__trace("tight bound %d (diagonal %d)\n", tb, diag_cost);
   }
 
   // see if the problem is symmetric
@@ -317,11 +310,11 @@ int main(int argc, char *argv[])
 
   if (symmetrical)
     if (symmetrical0)
-      _fd_debug("symmetrical problem with 0 diagonal\n");
+      fd__trace("symmetrical problem with 0 diagonal\n");
     else
-      _fd_debug("symmetrical problem\n");
+      fd__trace("symmetrical problem\n");
   else
-    _fd_debug("asymmetrical problem (%d, %d)\n", l, c);
+    fd__trace("asymmetrical problem (%d, %d)\n", l, c);
 
   if (generic)
     symmetrical0 = 0;
@@ -405,7 +398,7 @@ int main(int argc, char *argv[])
   // the *transpose* of the distance matrix
   for (l = 0; l < N; ++l)
     for (c = 0; c < N; ++c)
-      distt[l][c] = FD_CONST(Pdist(c, l));
+      distt[l][c] = fd_const(Pdist(c, l));
 
   for (l = 0; l < N; ++l)
     for (c = 0; c < N; ++c)
@@ -430,7 +423,7 @@ int main(int argc, char *argv[])
 
       for (l = 0; l < N; ++l)
 	for (c = 0; c < N; ++c)
-	  distt[l][c] = FD_CONST(Pflow(l, c));
+	  distt[l][c] = fd_const(Pflow(l, c));
 
       for (l = 0; l < N; ++l)
 	fd_knapsack2(ndist[l], distt[l], N, sums[l]);
@@ -443,7 +436,7 @@ int main(int argc, char *argv[])
 	for (c = 0; c < N; ++c)
 	  {
 	    prods[0][i] = ndist[l][c];
-	    prods[1][i] = FD_CONST(Pflow(l, c));
+	    prods[1][i] = fd_const(Pflow(l, c));
 
 	    ++i;
 	  }
@@ -471,7 +464,7 @@ int main(int argc, char *argv[])
 
       for (l = 1; l < N; ++l)
 	for (c = 0; c < l; ++c)
-	  distt[l][c] = FD_CONST(Pflow(l, c));
+	  distt[l][c] = fd_const(Pflow(l, c));
 
       for (l = 1; l < N; ++l)
 	fd_knapsack2(ndist[l], distt[l], l, sums[l - 1]);
@@ -484,7 +477,7 @@ int main(int argc, char *argv[])
 	for (c = 0; c < l; ++c)
 	  {
 	    prods[0][i] = ndist[l][c];
-	    prods[1][i] = FD_CONST(Pflow(l, c));
+	    prods[1][i] = fd_const(Pflow(l, c));
 
 	    ++i;
 	  }
@@ -499,7 +492,7 @@ int main(int argc, char *argv[])
 	for (c = 0; c < l; ++c)
 	  {
 	    prods[0][i] = ndist[l][c];
-	    prods[1][i] = FD_CONST(Pflow(l, c));
+	    prods[1][i] = fd_const(Pflow(l, c));
 
 	    ++i;
 	  }
@@ -540,7 +533,7 @@ int main(int argc, char *argv[])
 
   // distances
   for (i = 0; i < N * N; ++i)
-    dist[i] = FD_CONST(P->dist[i]);
+    dist[i] = fd_const(P->dist[i]);
 
   // costs
 
@@ -585,8 +578,8 @@ int main(int argc, char *argv[])
 
       for (l = 0; l < N; ++l)
 	{
-	  new_dist[l * N + l] = FD_CONST(0);
-	  npos[l * N + l] = FD_CONST(0);
+	  new_dist[l * N + l] = fd_const(0);
+	  npos[l * N + l] = fd_const(0);
 	}
 
       for (l = 1; l < N; ++l)
diff --git a/bench/queens.c b/bench/queens.c
index 4b6b4c0..7ba9077 100644
--- a/bench/queens.c
+++ b/bench/queens.c
@@ -1,4 +1,5 @@
 #include <stdio.h>
+#include <stdlib.h>
 #include <string.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -32,7 +33,7 @@ int main(int argc, char *argv[])
       }
 
   if (N > NMAX)
-    _fd_fatal("queens: more than NMAX queens requested");
+    fd__fatal("queens: more than NMAX queens requested");
 
 #ifdef LOCAL_SEARCH
 #if 01
@@ -42,13 +43,13 @@ int main(int argc, char *argv[])
   //seed = 0;
 #else
     if ((dev_random = open("/dev/urandom", O_RDONLY)) == -1)
-    _fd_fatal("/dev/urandom: %s\n", strerror(errno));
+    fd__fatal("/dev/urandom: %s\n", strerror(errno));
 
   read(dev_random, &seed, sizeof(seed));
 #endif
 
   srandom(seed);
-  _fd_debug("seed = %u\n", seed);
+  fd__trace("seed = %u\n", seed);
 #endif
 
 /*
@@ -104,7 +105,7 @@ int main(int argc, char *argv[])
 	  {
 	    int j;
 
-	    _fd_error("\nsolution contains non-singleton variable\n");
+	    fd__error("\nsolution contains non-singleton variable\n");
 
 
 	    for (j = 0; j < N; ++j)
diff --git a/bench/queens2.c b/bench/queens2.c
index 48e82e0..1750391 100644
--- a/bench/queens2.c
+++ b/bench/queens2.c
@@ -1,10 +1,12 @@
 /* solves two unconnected queens problems */
 
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 #include <fcntl.h>
 #include <errno.h>
 
-#include "fdc_int.h"
+#include <paccs.h>
 
 #define NMAX 200		// maximum number of queens
 
@@ -75,8 +77,6 @@ int main(int argc, char *argv[])
   fd_int queens[2 * NMAX];
   int solutions = 0, one_solution = 1;
   int i, j;
-  int seed;
-  int dev_random;
 
   fd_init(&argc, &argv);
 
@@ -92,20 +92,23 @@ int main(int argc, char *argv[])
       }
 
 #ifdef LOCAL_SEARCH
+  int seed;
 #if 01
   seed = time(0);
   seed = 1206987119;
   //seed = 1207917731; // N = 6, best improvement termina
   //seed = 0;
 #else
-    if ((dev_random = open("/dev/urandom", O_RDONLY)) == -1)
-    _fd_fatal("/dev/urandom: %s\n", strerror(errno));
+  int dev_random;
+
+  if ((dev_random = open("/dev/urandom", O_RDONLY)) == -1)
+    fd__fatal("/dev/urandom: %s\n", strerror(errno));
 
   read(dev_random, &seed, sizeof(seed));
 #endif
 
   srandom(seed);
-  _fd_debug("seed = %u\n", seed);
+  fd__trace("seed = %u\n", seed);
 #endif
 
   init_queens(queens, N1);
@@ -120,7 +123,7 @@ int main(int argc, char *argv[])
 #if 01
       for (i = 0; i < N1; ++i)
 	if (!fd_var_single(queens[i], NULL))
-	  _fd_fatal("solution contains non-singleton variable");
+	  fd__fatal("solution contains non-singleton variable");
 #endif
 
       for (i = 0; i < N1; ++i)
@@ -135,7 +138,7 @@ int main(int argc, char *argv[])
 #if 01
       for (i = N1; i < N1 + N2; ++i)
 	if (!fd_var_single(queens[i], NULL))
-	  _fd_fatal("solution contains non-singleton variable");
+	  fd__fatal("solution contains non-singleton variable");
 #endif
 
       for (i = N1; i < N1 + N2; ++i)
diff --git a/bench/sudoku.c b/bench/sudoku.c
index 2506915..02cd2ea 100644
--- a/bench/sudoku.c
+++ b/bench/sudoku.c
@@ -1,6 +1,8 @@
 #include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
 
-#include "fdc_int.h"
+#include <paccs.h>
 
 #define floor_sqrt(s, n) {for ((s) = 0; (s) * (s) <= (n); ++(s)); --(s);}
 
@@ -316,7 +318,7 @@ void constrain_piece(fd_int *variables, int nvariables)
 #endif
 }
 
-main(int argc, char *argv[])
+int main(int argc, char *argv[])
 {
   fd_int board[N][N];
   sudoku_i *problem;
@@ -324,16 +326,17 @@ main(int argc, char *argv[])
   int sqrN;
   int solutions = 0, one_solution = 1;
   int i, j, np = -1;
-  int seed;
 
   fd_init(&argc, &argv);
 
 #ifdef LOCAL_SEARCH
+  int seed;
+
   seed = time(0);
   //seed = 1206468701;
   seed = 1208196137; // converges to a local minimum with MCH
   srandom(seed);
-  _fd_debug("seed = %u\n", seed);
+  fd__trace("seed = %u\n", seed);
 #endif
 
   for (i = 1; i < argc; ++i)
diff --git a/bench/times.c b/bench/times.c
index 8fd7047..18bb4b0 100644
--- a/bench/times.c
+++ b/bench/times.c
@@ -1,9 +1,8 @@
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
-#include <fdc_int.h>
-#include "variables.h"
-#include "values.h"
+#include <paccs.h>
 
 int main(int argc, char *argv[])
 {
diff --git a/src/VERSION b/src/VERSION
index ecde812..0ac647c 100644
--- a/src/VERSION
+++ b/src/VERSION
@@ -1 +1 @@
-0.90d
+0.91
diff --git a/src/agents-splitgo-mpi.c b/src/agents-splitgo-mpi.c
index 9d2add4..9188b28 100644
--- a/src/agents-splitgo-mpi.c
+++ b/src/agents-splitgo-mpi.c
@@ -17,6 +17,8 @@
 #include "splitting.h"
 #include "bound.h"
 
+#include "util.h"
+
 #ifndef COMPACT_DOMAINS
 #error "only works with COMPACT_DOMAINS"
 #endif
@@ -135,7 +137,7 @@ void _fd_statistics_msgs()
 {
   extern int tid;
 
-  _fd_output("[%d] messages: %lu sent, %lu received\n", tid, _fd_msgs_sent,
+  fd__output("[%d] messages: %lu sent, %lu received\n", tid, _fd_msgs_sent,
 	     _fd_msgs_received);
 }
 #else /* STATS_MSGS */
@@ -214,7 +216,7 @@ int _fd_agent(int n)
       int result;
       struct timeval ti, tis, to, tos;
 
-      _fd_debug("[%d.%d] agent starting\n", tid, n);
+      fd__trace("[%d.%d] agent starting\n", tid, n);
 
       gettimeofday(&ti, NULL);
 
@@ -238,17 +240,17 @@ int _fd_agent(int n)
 	gettimeofday(&to, NULL);
 	timersub(&to, &tis, &tos);
 	timersub(&to, &ti, &to);
-	_fd_debug("[%d.%d] took %d.%06ds (%d.%06ds)\n", tid, n,
+	fd__trace("[%d.%d] took %d.%06ds (%d.%06ds)\n", tid, n,
 		  tos.tv_sec, tos.tv_usec, to.tv_sec, to.tv_usec);
 
 	while (result == FD_OK)
 	  {
-	    _fd_debug("[%d.%d] agent was successful\n", tid, n);
+	    fd__trace("[%d.%d] agent was successful\n", tid, n);
 
 	    // ensure that only one agent signals its success
 	    // XXX: only good (?) if only looking for the 1st solution
 	    pthread_mutex_lock(&success_mutex);
-	    _fd_debug("[%d.%d] acquired success mutex\n", tid, n);
+	    fd__trace("[%d.%d] acquired success mutex\n", tid, n);
 
 	    // tell the main thread that this agent is done
 	    if (sem_wait(&ready_semaphore))
@@ -260,14 +262,14 @@ int _fd_agent(int n)
 	      perror("sem_post");
 
 	    pthread_mutex_unlock(&success_mutex);
-	    _fd_debug("[%d.%d] released success mutex\n", tid, n);
+	    fd__trace("[%d.%d] released success mutex\n", tid, n);
 
 	    if (!_fd_optimising)
 	      break;
 
 	    if (sem_wait(&resume_semaphore))
 	      perror("sem_post (resume)");
-	    _fd_debug("[%d.%d] resuming, new bound is %d\n", tid, n, _fd_bound_value());
+	    fd__trace("[%d.%d] resuming, new bound is %d\n", tid, n, _fd_bound_value());
 
 	    // find the next solution
 	    gettimeofday(&tis, NULL);
@@ -277,7 +279,7 @@ int _fd_agent(int n)
 	    gettimeofday(&to, NULL);
 	    timersub(&to, &tis, &tos);
 	    timersub(&to, &ti, &to);
-	    _fd_debug("[%d.%d] took %d.%06ds (%d.%06ds)\n", tid, n,
+	    fd__trace("[%d.%d] took %d.%06ds (%d.%06ds)\n", tid, n,
 		      tos.tv_sec, tos.tv_usec, to.tv_sec, to.tv_usec);
 	  }
 
@@ -286,7 +288,7 @@ int _fd_agent(int n)
 	  break;
 
 	// eventually, the agent will fail to find a solution
-	_fd_debug("[%d.%d] agent was unsuccessful\n", tid, n);
+	fd__trace("[%d.%d] agent was unsuccessful\n", tid, n);
 
 	// tell the main thread that this agent is done
 	if (sem_wait(&ready_semaphore))
@@ -304,7 +306,7 @@ int _fd_agent(int n)
 #if STEAL_WORK >= 2
 	do {
 	  // wait for more work
-	  _fd_debug("[%d.%d] waiting for more work\n", tid, n);
+	  fd__trace("[%d.%d] waiting for more work\n", tid, n);
 	  pthread_cond_wait(&continue_cond, &continue_mutex);
 	} while(n >= agents_to_run);
 
@@ -325,7 +327,7 @@ int _fd_agent(int n)
       unsigned long long solutions;
       struct timeval ti, tis, to, tos;
 
-      _fd_debug("[%d.%d] agent starting\n", tid, n);
+      fd__trace("[%d.%d] agent starting\n", tid, n);
 
       gettimeofday(&ti, NULL);
 
@@ -346,10 +348,10 @@ int _fd_agent(int n)
 	gettimeofday(&to, NULL);
 	timersub(&to, &tis, &tos);
 	timersub(&to, &ti, &to);
-	_fd_debug("[%d.%d] took %d.%06ds (%d.%06ds)\n", tid, n,
+	fd__trace("[%d.%d] took %d.%06ds (%d.%06ds)\n", tid, n,
 		  tos.tv_sec, tos.tv_usec, to.tv_sec, to.tv_usec);
 
-	_fd_debug("[%d.%d] found %llu solutions\n", tid, n, solutions);
+	fd__trace("[%d.%d] found %llu solutions\n", tid, n, solutions);
 
 	// tell the main thread that this agent is done
 	if (sem_wait(&ready_semaphore))
@@ -370,7 +372,7 @@ int _fd_agent(int n)
 #if STEAL_WORK >= 2
 	do {
 	  // wait for more work
-	  _fd_debug("[%d.%d] waiting for more work\n", tid, n);
+	  fd__trace("[%d.%d] waiting for more work\n", tid, n);
 	  pthread_cond_wait(&continue_cond, &continue_mutex);
 	} while (n >= agents_to_run);
 
@@ -406,7 +408,7 @@ static void _fd_send(int to, int tag)
   MPI_Request mpi_request = MPI_REQUEST_NULL;
 
   if (MPI_Isend(NULL, 0, MPI_CHAR, to, tag, MPI_COMM_WORLD, &mpi_request))
-    _fd_fatal("MPI_Isend failed");
+    fd__fatal("MPI_Isend failed");
 }
 
 /* send TO a message with data (non-blocking) */
@@ -415,7 +417,7 @@ static void _fd_send_data(int to, int tag, void *buf, int n, MPI_Datatype type)
   MPI_Request mpi_request = MPI_REQUEST_NULL;
 
   if (MPI_Isend(buf, n, type, to, tag, MPI_COMM_WORLD, &mpi_request))
-    _fd_fatal("MPI_Isend failed");
+    fd__fatal("MPI_Isend failed");
 }
 
 #if STEAL_WORK >= 2
@@ -427,26 +429,26 @@ static void _fd_ssend_data(int to, int tag, void *buf, int n, MPI_Datatype type,
     {
 #ifndef MAD_MPI
       if (MPI_Issend(buf, n, type, to, tag, MPI_COMM_WORLD, request))
-	_fd_fatal("MPI_Issend failed");
+	fd__fatal("MPI_Issend failed");
 
 #if 0
       {
 	int mpi_flag;
 
-	_fd_debug("[%d] MPI_Test = %d\n", tid,
+	fd__debug("[%d] MPI_Test = %d\n", tid,
 		  MPI_Test(request, &mpi_flag, MPI_STATUS_IGNORE));
 
 	if (mpi_flag)
-	  _fd_debug("[%d] send completed\n", tid);
+	  fd__debug("[%d] send completed\n", tid);
 	else
-	  _fd_debug("[%d] send didn't complete yet\n", tid);
+	  fd__debug("[%d] send didn't complete yet\n", tid);
       }
 #endif
 #else /* MAD_MPI */
       // XXX: NMAD doesn't have MPI_Issend
       // XXX: no record of the sending is left
       if (MPI_Ssend(buf, n, type, to, tag, MPI_COMM_WORLD))
-	_fd_fatal("MPI_Send failed");
+	fd__fatal("MPI_Send failed");
 
       *request = MPI_REQUEST_NULL;
 #endif /* MAD_MPI */
@@ -456,7 +458,7 @@ static void _fd_ssend_data(int to, int tag, void *buf, int n, MPI_Datatype type,
       MPI_Request mpi_request = MPI_REQUEST_NULL;
 
       if (MPI_Isend(buf, n, type, to, tag, MPI_COMM_WORLD, &mpi_request))
-	_fd_fatal("MPI_Isend failed");
+	fd__fatal("MPI_Isend failed");
     }
 }
 #endif /* STEAL_WORK >= 2 */
@@ -466,7 +468,7 @@ static void fd__send_empty_store(int to, int tag, _fd_store buffer,
 {
   if (MPI_Isend(buffer, 0, MPI_DOMAIN_TYPE,
 		to, tag, MPI_COMM_WORLD, request))
-    _fd_fatal("MPI_Isend failed");
+    fd__fatal("MPI_Isend failed");
 }
 
 static void _fd_send_store(int to, int tag, _fd_store buffer,
@@ -481,7 +483,7 @@ static void _fd_send_store(int to, int tag, _fd_store buffer,
     {
       if (MPI_Isend(buffer, fd_variables_count * DOMAIN_WORDS, MPI_DOMAIN_TYPE,
 		    to, tag, MPI_COMM_WORLD, request))
-	_fd_fatal("MPI_Isend failed");
+	fd__fatal("MPI_Isend failed");
     }
 }
 
@@ -492,7 +494,7 @@ static int _fd_recv_store(int from, int tag, _fd_store buffer)
 
   if (MPI_Recv(buffer, fd_variables_count * DOMAIN_WORDS, MPI_DOMAIN_TYPE,
 	       from, tag, MPI_COMM_WORLD, &status))
-    _fd_fatal("MPI_Recv failed");
+    fd__fatal("MPI_Recv failed");
 
   MPI_Get_count(&status, MPI_DOMAIN_TYPE, &count);
 
@@ -511,7 +513,7 @@ static void _fd_broadcast(int msg, int processes, int from)
 	mpi_request = malloc(sizeof(MPI_Request));
 
 	if (MPI_Isend(NULL, 0, MPI_CHAR, i, msg, MPI_COMM_WORLD, mpi_request))
-	  _fd_fatal("MPI_Isend failed");
+	  fd__fatal("MPI_Isend failed");
 
 	fd_list_append(pending_requests, mpi_request);
       }
@@ -522,7 +524,7 @@ static void _fd_broadcast(int msg, int processes, int from)
   for (i = 0; i < processes; ++i)
     if (i != from)
       if (MPI_Isend(NULL, 0, MPI_CHAR, i, msg, MPI_COMM_WORLD, &mpi_request))
-	_fd_fatal("MPI_Isend failed");
+	fd__fatal("MPI_Isend failed");
 #endif
 }
 
@@ -560,9 +562,9 @@ static bool _fd_probe_external(int tag, char *name, int *source, bool consume)
   if (s)
     {
       char es[1024]; int _;
-      _fd_debug("MPI_Iprobe %s returned %d\n", name, s);
+      fd__debug("MPI_Iprobe %s returned %d\n", name, s);
       MPI_Error_string(s, es, &_);
-      _fd_debug("%s\n", es);
+      fd__debug("%s\n", es);
     }
 #endif
 
@@ -570,13 +572,13 @@ static bool _fd_probe_external(int tag, char *name, int *source, bool consume)
     {
       *source = mpi_status.MPI_SOURCE;
 
-      _fd_debug("[%d] got %s from %d\n", tid, name, *source);
+      fd__trace("[%d] got %s from %d\n", tid, name, *source);
 
       // consume message
       if (consume)
 	if (MPI_Recv(NULL, 0, MPI_CHAR, mpi_status.MPI_SOURCE, tag,
 		     MPI_COMM_WORLD, &mpi_status))
-	  _fd_fatal("MPI_Recv failed");
+	  fd__fatal("MPI_Recv failed");
 
       return true;
     }
@@ -590,7 +592,7 @@ static int _fd_get_event(int running, int *source, int *timeout)
   double tin, tmout = 0;
   int event;
 
-  _fd_debug("[%d] getting next event\n", tid);
+  fd__trace("[%d] getting next event\n", tid);
 
   if (timeout && *timeout)
     {
@@ -606,14 +608,14 @@ static int _fd_get_event(int running, int *source, int *timeout)
 	  if (sem_trywait(&notify_semaphore))
 	    {
 	      if (errno == EAGAIN)
-		; //_fd_debug("[%d] notify semaphore is busy\n", tid);
+		; //fd__trace("[%d] notify semaphore is busy\n", tid);
 	      else
 		perror("sem_trywait (notify)");
 	    }
 	  else
 	    {
 	      // got something from one of the agents
-	      _fd_debug("[%d] got something from an agent\n", tid);
+	      fd__trace("[%d] got something from an agent\n", tid);
 
 	      if (!_fd_counting_solutions)
 		{
@@ -622,7 +624,7 @@ static int _fd_get_event(int running, int *source, int *timeout)
 		  if (sem_post(&ready_semaphore))
 		    perror("sem_post");
 
-		  _fd_debug("[%d] got an answer from %s%d\n", tid,
+		  fd__trace("[%d] got an answer from %s%d\n", tid,
 			    s == -1 ? "-" : "", s + (s < 0));
 
 		  if (s >= 0)
@@ -719,17 +721,17 @@ static int _fd_get_event(int running, int *source, int *timeout)
 		event = EV_EXT_READY;
 		break;
 	      default:
-		_fd_debug("[%d] unknown tag %d\n", tid, mpi_status.MPI_TAG);
-		_fd_fatal("unknown tag in message");
+		fd__debug("[%d] unknown tag %d\n", tid, mpi_status.MPI_TAG);
+		fd__fatal("unknown tag in message");
 	      }
 
-	    _fd_debug("[%d] got %s from %d\n", tid, name, *source);
+	    fd__trace("[%d] got %s from %d\n", tid, name, *source);
 
 	    // consume message
 	    if (consume)
 	      if (MPI_Recv(NULL, 0, MPI_CHAR, mpi_status.MPI_SOURCE,
 			   mpi_status.MPI_TAG, MPI_COMM_WORLD, &mpi_status))
-		_fd_fatal("MPI_Recv failed");
+		fd__fatal("MPI_Recv failed");
 
 	    break;
 	  }
@@ -862,22 +864,22 @@ static void _fd_flush_events(int procno)
   while (mpi_request = fd_list_remove(pending_requests))
     {
       if (MPI_Test(mpi_request, &mpi_flag, MPI_STATUS_IGNORE))
-	_fd_debug("[%d] MPI_Test failed\n", procno);
+	fd__debug("[%d] MPI_Test failed\n", procno);
 
       if (!mpi_flag)
 	{
 #if 0	// this is probably the solution just sent, not cancelling it
-	  _fd_debug("[%d] cancelling request\n", procno);
+	  fd__trace("[%d] cancelling request\n", procno);
 
 	  if (MPI_Cancel(mpi_request))
-	    _fd_debug("[%d] MPI_Cancel failed\n", procno);
+	    fd__debug("[%d] MPI_Cancel failed\n", procno);
 
-	  _fd_debug("[%d] waiting for cancellation completion\n", procno);
+	  fd__trace("[%d] waiting for cancellation completion\n", procno);
 
 	  if (MPI_Wait(mpi_request, MPI_STATUS_IGNORE))
-	    _fd_debug("[%d] MPI_Wait failed\n", procno);
+	    fd__debug("[%d] MPI_Wait failed\n", procno);
 #else
-	  _fd_debug("[%d] found a not completed request\n", procno);
+	  fd__debug("[%d] found a not completed request\n", procno);
 #endif
 	}
 
@@ -911,7 +913,7 @@ static void _fd_flush_events(int procno)
 	  name = "FD_MSG_SOLUTION";
 	  type = MPI_DOMAIN_TYPE;
 	  count = fd_variables_count * DOMAIN_WORDS;
-	  _fd_error("[%d] dropping %s from %d\n", tid, name, source);
+	  fd__error("[%d] dropping %s from %d\n", tid, name, source);
 	  break;
 	case FD_MSG_FAIL:
 	  name = "FD_MSG_FAIL";
@@ -928,13 +930,13 @@ static void _fd_flush_events(int procno)
 	  name = "FD_MSG_COUNT";
 	  type = MPI_LONG_LONG;
 	  count = 1;
-	  _fd_error("[%d] dropping %s from %d\n", tid, name, source);
+	  fd__error("[%d] dropping %s from %d\n", tid, name, source);
 	  break;
 	case FD_MSG_STORE:
 	  name = "FD_MSG_STORE";
 	  type = MPI_DOMAIN_TYPE;
 	  count = fd_variables_count * DOMAIN_WORDS;
-	  _fd_error("[%d] *** dropping %s from %d\n", tid, name, source);
+	  fd__error("[%d] *** dropping %s from %d\n", tid, name, source);
 	  break;
 	case FD_MSG_FEED_ME:
 	  name = "FD_MSG_FEED_ME";
@@ -955,11 +957,11 @@ static void _fd_flush_events(int procno)
 	  name = "FD_MSG_READY";
 	  break;
 	default:
-	  _fd_debug("[%d] unknown tag %d\n", tid, mpi_status.MPI_TAG);
-	  _fd_fatal("unknown tag in message");
+	  fd__debug("[%d] unknown tag %d\n", tid, mpi_status.MPI_TAG);
+	  fd__fatal("unknown tag in message");
 	}
 
-      _fd_debug("[%d] dropping %s from %d\n", tid, name, source);
+      fd__debug("[%d] dropping %s from %d\n", tid, name, source);
 
       // consume message
       {
@@ -967,7 +969,7 @@ static void _fd_flush_events(int procno)
 
 	if (MPI_Recv(buffer, count, type, source, mpi_status.MPI_TAG,
 		     MPI_COMM_WORLD, MPI_STATUS_IGNORE))
-	  _fd_error("[%d] MPI_Recv failed (dropping %s)\n", tid, name);
+	  fd__error("[%d] MPI_Recv failed (dropping %s)\n", tid, name);
       }
     }
 #else /* MAD_MPI */
@@ -986,7 +988,7 @@ static void _fd_flush_events(int procno)
 	{
 	  _fd_store null = alloca(fd_variables_count * sizeof(*null));
 
-	  _fd_debug("[%d] dropping FD_MSG_SOLUTION from %d\n", procno, mpi_status.MPI_SOURCE);
+	  fd__trace("[%d] dropping FD_MSG_SOLUTION from %d\n", procno, mpi_status.MPI_SOURCE);
 
 	  _fd_recv_store(mpi_status.MPI_SOURCE, FD_MSG_SOLUTION, null);
 
@@ -997,12 +999,12 @@ static void _fd_flush_events(int procno)
 
       if (mpi_flag)
 	{
-	  _fd_debug("[%d] dropping FD_MSG_FEED_ME from %d\n", procno, mpi_status.MPI_SOURCE);
+	  fd__trace("[%d] dropping FD_MSG_FEED_ME from %d\n", procno, mpi_status.MPI_SOURCE);
 
 	  // consume message
 	  if (MPI_Recv(NULL, 0, MPI_CHAR, mpi_status.MPI_SOURCE, FD_MSG_FEED_ME,
 		       MPI_COMM_WORLD, &mpi_status))
-	    _fd_fatal("MPI_Recv failed");
+	    fd__fatal("MPI_Recv failed");
 
 	  got_one++;
 	}
@@ -1011,12 +1013,12 @@ static void _fd_flush_events(int procno)
 
       if (mpi_flag)
 	{
-	  _fd_debug("[%d] dropping FD_MSG_NO_WORK from %d\n", procno, mpi_status.MPI_SOURCE);
+	  fd__trace("[%d] dropping FD_MSG_NO_WORK from %d\n", procno, mpi_status.MPI_SOURCE);
 
 	  // consume message
 	  if (MPI_Recv(NULL, 0, MPI_CHAR, mpi_status.MPI_SOURCE, FD_MSG_NO_WORK,
 		       MPI_COMM_WORLD, &mpi_status))
-	    _fd_fatal("MPI_Recv failed");
+	    fd__fatal("MPI_Recv failed");
 
 	  got_one++;
 	}
@@ -1027,7 +1029,7 @@ static void _fd_flush_events(int procno)
 	{
 	  _fd_store null = alloca(fd_variables_count * sizeof(*null));
 
-	  _fd_debug("[%d] dropping FD_MSG_STORE from %d\n", procno, mpi_status.MPI_SOURCE);
+	  fd__trace("[%d] dropping FD_MSG_STORE from %d\n", procno, mpi_status.MPI_SOURCE);
 
 	  _fd_recv_store(mpi_status.MPI_SOURCE, FD_MSG_STORE, null);
 
@@ -1038,12 +1040,12 @@ static void _fd_flush_events(int procno)
 
       if (mpi_flag)
 	{
-	  _fd_debug("[%d] dropping FD_MSG_FAIL from %d\n", procno, mpi_status.MPI_SOURCE);
+	  fd__trace("[%d] dropping FD_MSG_FAIL from %d\n", procno, mpi_status.MPI_SOURCE);
 
 	  // consume message
 	  if (MPI_Recv(NULL, 0, MPI_CHAR, mpi_status.MPI_SOURCE, FD_MSG_FAIL,
 		       MPI_COMM_WORLD, &mpi_status))
-	    _fd_fatal("MPI_Recv failed");
+	    fd__fatal("MPI_Recv failed");
 
 	  got_one++;
 	}
@@ -1052,12 +1054,12 @@ static void _fd_flush_events(int procno)
 
       if (mpi_flag)
 	{
-	  _fd_debug("[%d] dropping FD_MSG_DONE from %d\n", procno, mpi_status.MPI_SOURCE);
+	  fd__trace("[%d] dropping FD_MSG_DONE from %d\n", procno, mpi_status.MPI_SOURCE);
 
 	  // consume message
 	  if (MPI_Recv(NULL, 0, MPI_CHAR, mpi_status.MPI_SOURCE, FD_MSG_DONE,
 		       MPI_COMM_WORLD, &mpi_status))
-	    _fd_fatal("MPI_Recv failed");
+	    fd__fatal("MPI_Recv failed");
 
 	  got_one++;
 	}
@@ -1066,12 +1068,12 @@ static void _fd_flush_events(int procno)
 
       if (mpi_flag)
 	{
-	  _fd_debug("[%d] dropping FD_MSG_SHARE from %d\n", procno, mpi_status.MPI_SOURCE);
+	  fd__trace("[%d] dropping FD_MSG_SHARE from %d\n", procno, mpi_status.MPI_SOURCE);
 
 	  // consume message
 	  if (MPI_Recv(NULL, 0, MPI_CHAR, mpi_status.MPI_SOURCE, FD_MSG_SHARE,
 		       MPI_COMM_WORLD, &mpi_status))
-	    _fd_fatal("MPI_Recv failed");
+	    fd__fatal("MPI_Recv failed");
 
 	  got_one++;
 	}
@@ -1080,12 +1082,12 @@ static void _fd_flush_events(int procno)
 
       if (mpi_flag)
 	{
-	  _fd_debug("[%d] dropping FD_MSG_NO_SHARE from %d\n", procno, mpi_status.MPI_SOURCE);
+	  fd__trace("[%d] dropping FD_MSG_NO_SHARE from %d\n", procno, mpi_status.MPI_SOURCE);
 
 	  // consume message
 	  if (MPI_Recv(NULL, 0, MPI_CHAR, mpi_status.MPI_SOURCE, FD_MSG_NO_SHARE,
 		       MPI_COMM_WORLD, &mpi_status))
-	    _fd_fatal("MPI_Recv failed");
+	    fd__fatal("MPI_Recv failed");
 
 	  got_one++;
 	}
@@ -1102,45 +1104,45 @@ void _fd_cleanup_mpi_state(int process)
       // a still running process has a chance to see it
       // XXX: New Madeleine can't cope with messages en route
 #if 0 //#ifndef MAD_MPI
-      _fd_debug("[%d] setting up barrier 1\n", process);
+      fd__trace("[%d] setting up barrier 1\n", process);
       if (MPI_Barrier(MPI_COMM_WORLD))
-	_fd_error("[%d] MPI_Barrier failed\n", process);
+	fd__error("[%d] MPI_Barrier failed\n", process);
       else
-	_fd_debug("[%d] leaving barrier 1\n", process);
+	fd__trace("[%d] leaving barrier 1\n", process);
 #endif
 
       _fd_flush_events(process);
 
 #if 0
-      _fd_debug("[%d] setting up barrier 2\n", process);
+      fd__trace("[%d] setting up barrier 2\n", process);
       if (MPI_Barrier(MPI_COMM_WORLD))
-	_fd_error("[%d] MPI_Barrier failed\n", process);
+	fd__error("[%d] MPI_Barrier failed\n", process);
       else
-	_fd_debug("[%d] leaving barrier 2\n", process);
+	fd__trace("[%d] leaving barrier 2\n", process);
 #endif
     }
 
   _fd_statistics_msgs();
 
-  _fd_debug("[%d] calling MPI_Finalize\n", process);
+  fd__trace("[%d] calling MPI_Finalize\n", process);
   if (MPI_Finalize())
-    _fd_error("[%d] MPI_Finalize failed\n", process);
+    fd__error("[%d] MPI_Finalize failed\n", process);
   else
-    _fd_debug("[%d] returned from MPI_Finalize\n", process);
+    fd__trace("[%d] returned from MPI_Finalize\n", process);
 }
 
 static void _fd_exit_process(int process)
 {
   _fd_cleanup_mpi_state(process);
 
-  _fd_debug("[%d] exiting\n", process);
+  fd__trace("[%d] exiting\n", process);
 
   exit(0);
 }
 
 void _fd_search_space_sizes(_fd_store stores[], int n)
 {
-#ifndef FAST
+#ifdef TRACE
   double s;
   int i, v;
 
@@ -1155,14 +1157,14 @@ void _fd_search_space_sizes(_fd_store stores[], int n)
       for (v = 0; v < fd__label_vars_count; ++v)
 	s *= _fd_val_size(SVALUE(stores[i][fd__label_vars[v]->index]));
 
-      _fd_debug("[%d] search space %d size is %g\n", tid, i, s);
+      fd__trace("[%d] search space %d size is %g\n", tid, i, s);
     }
-#endif /* FAST */
+#endif /* TRACE */
 }
 
 static void count_singletons(_fd_store store)
 {
-#ifndef FAST
+#ifdef TRACE
   int i, n;
 
   n = 0;
@@ -1171,7 +1173,7 @@ static void count_singletons(_fd_store store)
     if (_fd_val_single(SVALUE(store[i]), NULL))
       ++n;
 
-  _fd_debug("[%d] store has %d/%d/%d singletons\n", tid, n,
+  fd__trace("[%d] store has %d/%d/%d singletons\n", tid, n,
 	    fd__label_vars_count, fd_variables_count);
 #endif
 }
@@ -1185,7 +1187,7 @@ static int _fd_restart_agents(int tid, int nagents)
   parts = fd__split_problem(nagents, _fd_agents_stores, fd__split_team_problem_f);
 
   if (parts < nagents)
-    _fd_debug("[%d] WARNING: store only good for %d agents (out of %d)\n",
+    fd__debug("[%d] WARNING: store only good for %d agents (out of %d)\n",
 	      tid, parts, nagents);
 
   agents_to_run = parts;
@@ -1200,7 +1202,7 @@ static int _fd_restart_agents(int tid, int nagents)
   if (pthread_mutex_unlock(&continue_mutex))
     perror("pthread_mutex_unlock");
 
-  _fd_debug("[%d] told waiting agents to proceed\n", tid);
+  fd__trace("[%d] told waiting agents to proceed\n", tid);
 
   return parts;
 }
@@ -1222,7 +1224,7 @@ static int _fd_poll_next(int last, int skip)
   if (poll >= 0)
     {
       // send a forceful request for work
-      _fd_debug("[%d] polling %d\n", tid, poll);
+      fd__trace("[%d] polling %d\n", tid, poll);
       _fd_send(poll, FD_MSG_SHARE);
     }
 
@@ -1261,7 +1263,7 @@ static int _fd_poll_rr(bool starting, int np_or_last, int skip)
     return -1;
 
   // send a forceful request for work
-  _fd_debug("[%d] polling %d\n", tid, poll);
+  fd__trace("[%d] polling %d\n", tid, poll);
   _fd_send(poll, FD_MSG_SHARE);
 
   last_polled = poll;
@@ -1286,7 +1288,7 @@ static bool _fd_share_work(int peer, MPI_Request *request, fd_list saved_stores,
   if (!mpi_flag)
     {
       // the previous send has not completed
-      _fd_debug("[%d] there's a store in transit to %d\n", tid, peer);
+      fd__trace("[%d] there's a store in transit to %d\n", tid, peer);
 
       /* despite MPI_Test() having failed, the store may already have
 	 been received and processed; since process 0 must receive or
@@ -1306,7 +1308,7 @@ static bool _fd_share_work(int peer, MPI_Request *request, fd_list saved_stores,
       memcpy(_fd_processes_stores[peer], saved,
 	     fd_variables_count * sizeof(*saved));
 
-      _fd_debug("[%d] sending store (saved) to %d\n", tid, peer);
+      fd__trace("[%d] sending store (saved) to %d\n", tid, peer);
       _fd_send_store(peer, FD_MSG_STORE, _fd_processes_stores[peer], request,
 		     true);
 
@@ -1315,13 +1317,9 @@ static bool _fd_share_work(int peer, MPI_Request *request, fd_list saved_stores,
       return true;
     }
 
-#ifndef INDEX_IN_POOL
-  if (_fd_steal_store(_fd_processes_stores[peer], -1, 0))
-#else
   if (_fd_steal_store(_fd_processes_stores[peer], NULL, -1, 0))
-#endif
     {
-      _fd_debug("[%d] sending store to %d\n", tid, peer);
+      fd__trace("[%d] sending store to %d\n", tid, peer);
       _fd_send_store(peer, FD_MSG_STORE, _fd_processes_stores[peer], request,
 		     true);
 
@@ -1331,12 +1329,12 @@ static bool _fd_share_work(int peer, MPI_Request *request, fd_list saved_stores,
   // tell the requester that there is no work available
   if (polled)
     {
-      _fd_debug("[%d] no work to share with %d\n", tid, peer);
+      fd__trace("[%d] no work to share with %d\n", tid, peer);
       _fd_send(peer, FD_MSG_NO_SHARE);
     }
   else
     {
-      _fd_debug("[%d] can't find work requested by %d\n", tid, peer);
+      fd__trace("[%d] can't find work requested by %d\n", tid, peer);
       _fd_send(peer, FD_MSG_NO_WORK);
     }
 
@@ -1355,13 +1353,13 @@ static bool _fd_share_work(int peer, MPI_Request *request, fd_list saved_stores,
 	      {								     \
 		/* record polling process as idle */			     \
 		gettimeofday(&to, NULL); timersub(&to, &ti, &to);	     \
-		_fd_debug("[%d] %d idled after %d.%06ds\n", tid, _p,	     \
+		fd__trace("[%d] %d idled after %d.%06ds\n", tid, _p,	     \
 			   to.tv_sec, to.tv_usec);			     \
 									     \
 		process_idle[_p] = true;				     \
 		if (++idle_processes == live_procs - 1)			     \
 		  {							     \
-		    _fd_debug("[%d] sending 2nd quit\n", tid);		     \
+		    fd__trace("[%d] sending 2nd quit\n", tid);		     \
 									     \
 		    _fd_broadcast(FD_MSG_QUIT, processes, tid);		     \
 		  }							     \
@@ -1401,21 +1399,21 @@ int _fd_dsolve()
   gettimeofday(&ti, NULL);
 
   if (MPI_Comm_rank(MPI_COMM_WORLD, &tid))
-    _fd_fatal("MPI_Comm_rank failed\n");
+    fd__fatal("MPI_Comm_rank failed\n");
 
-  _fd_debug("I'm MPI rank %d\n", tid);
+  fd__trace("I'm MPI rank %d\n", tid);
 
   {
     char host[512];
 
-    gethostname(host, sizeof(host));
-    _fd_debug("[%d] process %d at %s\n", tid, getpid(), host);
+    fd__trace("[%d] process %d at %s\n", tid, getpid(),
+	      (gethostname(host, sizeof(host)), host));
   }
 
   if (MPI_Comm_size(MPI_COMM_WORLD, &processes))
-    _fd_fatal("MPI_Comm_size failed\n");
+    fd__fatal("MPI_Comm_size failed\n");
 
-  _fd_debug("[%d] There are %d of us\n", tid, processes);
+  fd__trace("[%d] There are %d of us\n", tid, processes);
 
   pending_requests = fd_list_new();
 
@@ -1474,7 +1472,7 @@ int _fd_dsolve()
       parts = fd__split_problem(processes, _fd_processes_stores, fd__split_problem_f);
       _fd_search_space_sizes(_fd_processes_stores, parts);
       if (parts < processes)
-	_fd_error("WARNING: work enough for %d processes only\n", parts);
+	fd__error("WARNING: work enough for %d processes only\n", parts);
 
       // now send the resulting parts to the other processes
       // XXX: could each have done what this did and now pick its share?
@@ -1494,17 +1492,17 @@ int _fd_dsolve()
     {
       // code for the remaining processes
 
-      _fd_debug("[%d] waiting for initial store\n", tid);
+      fd__trace("[%d] waiting for initial store\n", tid);
 
       // retrieve the domains assigned to the process
       if (!_fd_recv_store(0, FD_MSG_STORE, store))
       	{
-	  _fd_debug("[%d] empty initial store\n", tid);
+	  fd__debug("[%d] empty initial store\n", tid);
 
 	  _fd_exit_process(tid);
 	}
 
-      _fd_debug("[%d] received initial store\n", tid);
+      fd__trace("[%d] received initial store\n", tid);
     }
 
 #if FILTER_DOMAINS > 1
@@ -1530,7 +1528,7 @@ int _fd_dsolve()
 
   if (parts < nagents)
     {
-      _fd_debug("[%d] reducing to %d agents!\n", tid, parts);
+      fd__trace("[%d] reducing to %d agents!\n", tid, parts);
 
       nagents = parts;
     }
@@ -1542,7 +1540,7 @@ int _fd_dsolve()
 
   gettimeofday(&to, NULL);
   timersub(&to, &ti, &to);
-  _fd_debug("[%d] setup took %d.%06ds\n", tid, to.tv_sec, to.tv_usec);
+  fd__trace("[%d] setup took %d.%06ds\n", tid, to.tv_sec, to.tv_usec);
 
 #if 0
   for (i = 0; i < nagents; ++i)
@@ -1593,7 +1591,7 @@ int _fd_dsolve()
 	    }
 	}
 
-      _fd_debug("[%d] main thread waiting\n", tid);
+      fd__trace("[%d] main thread waiting\n", tid);
 
       running = started;
       live_procs = processes;
@@ -1607,7 +1605,7 @@ int _fd_dsolve()
 	    {
 #if STEAL_WORK >= 2
 	    case EV_INT_TIMEOUT:
-	      _fd_debug("[%d] timed out waiting for event\n", tid);
+	      fd__trace("[%d] timed out waiting for event\n", tid);
 
 	      switch (waiting_on)
 		{
@@ -1644,7 +1642,7 @@ int _fd_dsolve()
 		  else
 		    {
 		      // resend request for work
-		      _fd_debug("[%d] re-asking for work\n", tid);
+		      fd__trace("[%d] re-asking for work\n", tid);
 		      _fd_broadcast(FD_MSG_FEED_ME, processes, tid);
 
 		      timeout = FEED2_TIMEOUT;
@@ -1687,8 +1685,8 @@ int _fd_dsolve()
 
 		  break;
 		default:
-		  _fd_debug("[%d] unknown timeout: %d\n", tid, waiting_on);
-		  _fd_fatal("unknown timeout reason");
+		  fd__debug("[%d] unknown timeout: %d\n", tid, waiting_on);
+		  fd__fatal("unknown timeout reason");
 		}
 
 	      break;
@@ -1697,7 +1695,7 @@ int _fd_dsolve()
 	    /* first solution internal events (from the workers) */
 
 	    case EV_INT_SUCCESS:
-	      _fd_debug("[%d] using agent %d's results\n", tid, peer);
+	      fd__trace("[%d] using agent %d's results\n", tid, peer);
 
 	      assert(!_fd_counting_solutions);
 
@@ -1706,7 +1704,7 @@ int _fd_dsolve()
 		  // check and update bound
 		  if (!_fd_bound_check_set(_fd_agents_stores[peer]))
 		    {
-		      _fd_debug("[%d] invalid solution, releasing %d\n", tid, peer);
+		      fd__trace("[%d] invalid solution, releasing %d\n", tid, peer);
 		      if (sem_post(&resume_semaphore))
 			perror("sem_post (resume)");
 
@@ -1720,11 +1718,11 @@ int _fd_dsolve()
 			 fd_variables_count * sizeof(*solution));
 
 		  // release agent
-		  _fd_debug("[%d] releasing agent %d\n", tid, peer);
+		  fd__trace("[%d] releasing agent %d\n", tid, peer);
 		  if (sem_post(&resume_semaphore))
 		    perror("sem_post (resume)");
 
-		  _fd_debug("[%d] bound updated to %d\n", tid, _fd_bound_value());
+		  fd__trace("[%d] bound updated to %d\n", tid, _fd_bound_value());
 
 		  // broadcast new bound
 		  bound = _fd_bound_value();
@@ -1746,7 +1744,7 @@ int _fd_dsolve()
 
 	      break;
 	    case EV_INT_FAIL:
-	      _fd_debug("[%d] agent %d found no solution\n", tid, peer);
+	      fd__trace("[%d] agent %d found no solution\n", tid, peer);
 
 	      assert(!_fd_counting_solutions);
 
@@ -1770,7 +1768,7 @@ int _fd_dsolve()
 			{
 			  if (!supplier)
 			    {
-			      _fd_debug("[%d] asking for work\n", tid);
+			      fd__trace("[%d] asking for work\n", tid);
 			      _fd_broadcast(FD_MSG_FEED_ME, processes, tid);
 
 			      waiting_on = FD_MSG_FEED_ME;
@@ -1802,7 +1800,7 @@ int _fd_dsolve()
 		  // see if there's some store saved up
 		  if (saved = fd_list_remove(saved_stores))
 		    {
-		      _fd_debug("[%d] using saved store\n", tid);
+		      fd__trace("[%d] using saved store\n", tid);
 
 		      memcpy(store, saved,
 			     fd_variables_count * sizeof(*saved));
@@ -1840,7 +1838,7 @@ int _fd_dsolve()
 		      if (!supplier)
 			{
 			  // ask the other processes for more work
-			  _fd_debug("[%d] asking for work\n", tid);
+			  fd__trace("[%d] asking for work\n", tid);
 			  _fd_broadcast(FD_MSG_FEED_ME, processes, tid);
 
 			  waiting_on = FD_MSG_FEED_ME;
@@ -1873,18 +1871,18 @@ int _fd_dsolve()
 	    /* first solution external events (from the other processes) */
 
 	    case EV_EXT_SUCCESS:
-	      _fd_debug("[%d] received a solution from %d\n", tid, peer);
+	      fd__trace("[%d] received a solution from %d\n", tid, peer);
 
 	      assert(!_fd_counting_solutions);
 
 	      _fd_recv_store(peer, FD_MSG_SOLUTION, store);
 
-	      _fd_debug("[%d] using process %d's results\n", tid, peer);
+	      fd__trace("[%d] using process %d's results\n", tid, peer);
 
 	      gettimeofday(&to, NULL);
 	      timersub(&to, &ti, &to);
-#if defined(STATS_PROCS) || !defined(FAST)
-	      fd__info("[%d] process %d took %d.%06ds\n", tid, peer, to.tv_sec, to.tv_usec);
+#if defined(STATS_PROCS) || defined(TRACE)
+	      fd__debug("[%d] process %d took %d.%06ds\n", tid, peer, to.tv_sec, to.tv_usec);
 #endif
 
 	      if (_fd_optimising)
@@ -1892,7 +1890,7 @@ int _fd_dsolve()
 		  // see if it is the better solution
 		  if (!have_solution || _fd_better_solution(store, solution))
 		    {
-		      _fd_debug("[%d] process %d solution is better\n", tid, peer);
+		      fd__trace("[%d] process %d solution is better\n", tid, peer);
 
 		      memcpy(solution, store,
 			     fd_variables_count * sizeof(*store));
@@ -1916,14 +1914,14 @@ int _fd_dsolve()
 
 	      break;
 	    case EV_EXT_FAIL:
-	      _fd_debug("[%d] process %d found no solution\n", tid, peer);
+	      fd__trace("[%d] process %d found no solution\n", tid, peer);
 
 	      assert(!_fd_counting_solutions);
 
 	      gettimeofday(&to, NULL);
 	      timersub(&to, &ti, &to);
-#if defined(STATS_PROCS) || !defined(FAST)
-	      fd__info("[%d] process %d took %d.%06ds\n", tid, peer, to.tv_sec, to.tv_usec);
+#if defined(STATS_PROCS) || defined(TRACE)
+	      fd__debug("[%d] process %d took %d.%06ds\n", tid, peer, to.tv_sec, to.tv_usec);
 #endif
 
 	      live_procs--;
@@ -1943,7 +1941,7 @@ int _fd_dsolve()
 
 	      break;
 	    case EV_EXT_DONE:
-	      _fd_debug("[%d] process %d told me to stop\n", tid, peer);
+	      fd__trace("[%d] process %d told me to stop\n", tid, peer);
 
 	      assert(!_fd_counting_solutions);
 
@@ -1954,7 +1952,7 @@ int _fd_dsolve()
 	    /* optimisation external events (from the other processes) */
 
 	    case EV_EXT_BOUND:
-	      _fd_debug("[%d] received new bound from %d\n", tid, peer);
+	      fd__trace("[%d] received new bound from %d\n", tid, peer);
 
 	      assert(!_fd_counting_solutions && _fd_optimising);
 
@@ -1963,24 +1961,24 @@ int _fd_dsolve()
 
 		if (MPI_Recv(&bound, 1, MPI_INT, peer, FD_MSG_BOUND,
 			     MPI_COMM_WORLD, &mpi_status))
-		  _fd_fatal("MPI_Recv failed");
+		  fd__fatal("MPI_Recv failed");
 
 		// try to set new bound
 		if (_fd_set_bound(bound))
 		  {
-		    _fd_debug("[%d] new bound is %d\n", tid, bound);
+		    fd__trace("[%d] new bound is %d\n", tid, bound);
 
 		    // bound has been reset, invalidate previous solution
 		    if (have_solution && !_fd_bound_check(solution))
 		      {
-			_fd_debug("[%d] invalidating current solution\n", tid);
+			fd__trace("[%d] invalidating current solution\n", tid);
 
 			status = FD_NOSOLUTION;
 			have_solution = false;
 		      }
 		  }
 		else
-		  _fd_debug("[%d] invalid bound %d\n", tid, bound);
+		  fd__trace("[%d] invalid bound %d\n", tid, bound);
 	      }
 
 	      break;
@@ -1988,7 +1986,7 @@ int _fd_dsolve()
 	    /* counting solutions internal events (from the workers) */
 
 	    case EV_INT_COUNT:
-	      _fd_debug("[%d] agent %d is done\n", tid, peer);
+	      fd__trace("[%d] agent %d is done\n", tid, peer);
 
 	      assert(_fd_counting_solutions);
 
@@ -2012,7 +2010,7 @@ int _fd_dsolve()
 			{
 			  if (!supplier)
 			    {
-			      _fd_debug("[%d] asking for work\n", tid);
+			      fd__trace("[%d] asking for work\n", tid);
 			      _fd_broadcast(FD_MSG_FEED_ME, processes, tid);
 
 			      waiting_on = FD_MSG_FEED_ME;
@@ -2044,7 +2042,7 @@ int _fd_dsolve()
 		  // see if there's some store saved up
 		  if (saved = fd_list_remove(saved_stores))
 		    {
-		      _fd_debug("[%d] using saved store\n", tid);
+		      fd__trace("[%d] using saved store\n", tid);
 
 		      memcpy(store, saved,
 			     fd_variables_count * sizeof(*saved));
@@ -2082,7 +2080,7 @@ int _fd_dsolve()
 		      if (!supplier)
 			{
 			  // ask the other processes for more work
-			  _fd_debug("[%d] asking for work\n", tid);
+			  fd__trace("[%d] asking for work\n", tid);
 			  _fd_broadcast(FD_MSG_FEED_ME, processes, tid);
 
 			  waiting_on = FD_MSG_FEED_ME;
@@ -2115,7 +2113,7 @@ int _fd_dsolve()
 	    /* counting solutions external events (from the other processes) */
 
 	    case EV_EXT_COUNT:
-	      _fd_debug("[%d] receiving count from %d\n", tid, peer);
+	      fd__trace("[%d] receiving count from %d\n", tid, peer);
 
 	      assert(_fd_counting_solutions);
 
@@ -2124,17 +2122,17 @@ int _fd_dsolve()
 
 		if (MPI_Recv(&ull, 1, MPI_LONG_LONG, peer, FD_MSG_COUNT,
 			     MPI_COMM_WORLD, &mpi_status))
-		  _fd_fatal("MPI_Recv failed");
+		  fd__fatal("MPI_Recv failed");
 
 		external_solutions += ull;
 
-		_fd_debug("[%d] process %d found %llu solutions\n", tid, peer, ull);
+		fd__trace("[%d] process %d found %llu solutions\n", tid, peer, ull);
 	      }
 
 	      gettimeofday(&to, NULL);
 	      timersub(&to, &ti, &to);
-#if defined(STATS_PROCS) || !defined(FAST)
-	      fd__info("[%d] process %d took %d.%06ds\n", tid, peer, to.tv_sec, to.tv_usec);
+#if defined(STATS_PROCS) || defined(TRACE)
+	      fd__debug("[%d] process %d took %d.%06ds\n", tid, peer, to.tv_sec, to.tv_usec);
 #endif
 
 	      live_procs--;
@@ -2158,9 +2156,9 @@ int _fd_dsolve()
 	    /* work stealing events (external) */
 
 	    case EV_EXT_STORE:
-	      _fd_debug("[%d] process %d sent a store\n", tid, peer);
+	      fd__trace("[%d] process %d sent a store\n", tid, peer);
 
-if (waiting_on == FD_MSG_ANY) _fd_debug("[%d] recovered store\n", tid);
+if (waiting_on == FD_MSG_ANY) fd__trace("[%d] recovered store\n", tid);
 
 	      if (running == 0)
 		{
@@ -2198,7 +2196,7 @@ if (waiting_on == FD_MSG_ANY) _fd_debug("[%d] recovered store\n", tid);
 
 	      break;
 	    case EV_EXT_REQ_WORK:
-	      _fd_debug("[%d] process %d wants more work\n", tid, peer);
+	      fd__trace("[%d] process %d wants more work\n", tid, peer);
 
 	      if (supplier)
 		{
@@ -2216,7 +2214,7 @@ if (waiting_on == FD_MSG_ANY) _fd_debug("[%d] recovered store\n", tid);
 
 	      break;
 	    case EV_EXT_NO_WORK:
-	      _fd_debug("[%d] process %d can't supply work\n", tid, peer);
+	      fd__trace("[%d] process %d can't supply work\n", tid, peer);
 
 	      // no work sharing after having been told to stop
 	      if (quitting || ready)
@@ -2268,7 +2266,7 @@ if (waiting_on == FD_MSG_ANY) _fd_debug("[%d] recovered store\n", tid);
 
 	      break;
 	    case EV_EXT_NO_SHARE:
-	      _fd_debug("[%d] process %d can't share work\n", tid, peer);
+	      fd__trace("[%d] process %d can't share work\n", tid, peer);
 
 	      // no work sharing after having been told to stop
 	      if (quitting || ready)
@@ -2284,7 +2282,7 @@ if (waiting_on == FD_MSG_ANY) _fd_debug("[%d] recovered store\n", tid);
 
 	      if (peer != polling)
 		{
-		  _fd_debug("[%d] polling %d but got answer from %d\n",
+		  fd__trace("[%d] polling %d but got answer from %d\n",
 			    tid, polling, peer);
 
 		  // belated answer, ignore
@@ -2331,14 +2329,14 @@ if (waiting_on == FD_MSG_ANY) _fd_debug("[%d] recovered store\n", tid);
 
 	      break;
 	    case EV_EXT_REQ_SHARE:
-	      _fd_debug("[%d] process %d really wants work\n", tid, peer);
+	      fd__trace("[%d] process %d really wants work\n", tid, peer);
 
 	      // if we're idle, there's nothing to share
 	      if (running == 0 || quitting || ready) // XXX: could be < nagents
 		{
 		  // notify the asking process that there really isn't
 		  // work to be shared
-		  _fd_debug("[%d] no work to share with %d\n", tid, peer);
+		  fd__trace("[%d] no work to share with %d\n", tid, peer);
 		  _fd_send(peer, FD_MSG_NO_SHARE);
 
 		  break;
@@ -2352,14 +2350,14 @@ if (waiting_on == FD_MSG_ANY) _fd_debug("[%d] recovered store\n", tid);
 	      break;
 	    case EV_EXT_READY:
 	      // peer has no work and no outgoing communications pending
-	      _fd_debug("[%d] %d is done\n", tid, peer);
+	      fd__trace("[%d] %d is done\n", tid, peer);
 
 	      assert(tid == 0);
 
 	      mark_process_idle(peer);
 
 	      // XXX: in mark_process_idle()
-	      //_fd_debug("[%d] sending 2nd quit\n", tid);
+	      //fd__trace("[%d] sending 2nd quit\n", tid);
 	      //_fd_broadcast(FD_MSG_QUIT, processes, tid);
 
 	      //done = running == 0 && live_procs == 1;
@@ -2371,7 +2369,7 @@ if (waiting_on == FD_MSG_ANY) _fd_debug("[%d] recovered store\n", tid);
 	      break;
 	    case EV_EXT_QUIT:
 	      // possibilities for finding work have been exhausted
-	      _fd_debug("[%d] %d told me to stop\n", tid, peer);
+	      fd__trace("[%d] %d told me to stop\n", tid, peer);
 
 	      assert(tid != 0);
 
@@ -2385,12 +2383,12 @@ if (waiting_on == FD_MSG_ANY) _fd_debug("[%d] recovered store\n", tid);
 	      break;
 #endif /* STEAL_WORK >= 2 */
 	    default:
-	      _fd_debug("[%d] unknown event in main loop: %d\n", tid, event);
-	      _fd_fatal("unknown event in main loop");
+	      fd__debug("[%d] unknown event in main loop: %d\n", tid, event);
+	      fd__fatal("unknown event in main loop");
 	    }
 
 #if STEAL_WORK >= 2
-if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid, quitting, ready, timeout);
+if (done) fd__trace("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid, quitting, ready, timeout);
 
 	  // if it is important that the full search space is explored,
 	  // make an extra effort for receiving messages that may have
@@ -2400,7 +2398,7 @@ if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid
 	       _fd_optimising ||
 	       status != FD_OK && !stopped))
 	    {
-	      _fd_debug("[%d] checking for any event before leaving\n", tid);
+	      fd__trace("[%d] checking for any event before leaving\n", tid);
 
 	      done = 0;
 	      waiting_on = FD_MSG_ANY;
@@ -2438,14 +2436,14 @@ if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid
 
 		  if (!mpi_flag)	// request hasn't completed
 		    {
-		      _fd_debug("[%d] recovering outstanding store to %d\n",
+		      fd__trace("[%d] recovering outstanding store to %d\n",
 				tid, i);
 
 		      // try cancelling the send (or the cancel?)
 		      if (MPI_Cancel(stores_pending + i))
 			perror("MPI_Cancel");
 
-		      _fd_debug("[%d] cancelled request\n", tid);
+		      fd__trace("[%d] cancelled request\n", tid);
 
 		      // allow the cancel some time to complete
 		      do
@@ -2457,7 +2455,7 @@ if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid
 			  if (mpi_flag)
 			    break;
 
-			  _fd_debug("[%d] cancelling store to %d didn't complete\n",
+			  fd__trace("[%d] cancelling store to %d didn't complete\n",
 				    tid, i);
 
 			  if (--tries == 0)
@@ -2469,7 +2467,7 @@ if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid
 
 		      if (!mpi_flag)  // cancel didn't complete, give up for now
 			{
-			  _fd_debug("[%d] giving up cancelling store to %i for now\n",
+			  fd__trace("[%d] giving up cancelling store to %i for now\n",
 				    tid, i);
 
 			  // if no store is cancelled, will have to wait
@@ -2482,7 +2480,7 @@ if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid
 			  continue;
 			}
 
-		      _fd_debug("[%d] status = (%d, %d, %d, %d, %d)\n", tid,
+		      fd__trace("[%d] status = (%d, %d, %d, %d, %d)\n", tid,
 				mpi_status.MPI_SOURCE, mpi_status.MPI_TAG,
 				mpi_status.MPI_ERROR, mpi_status._count,
 				mpi_status._cancelled);
@@ -2491,12 +2489,12 @@ if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid
 
 		      if (!mpi_flag)
 			{
-			  _fd_debug("[%d] cancel did not succeed\n", tid);
+			  fd__trace("[%d] cancel did not succeed\n", tid);
 
 			  continue;
 			}
 
-		      _fd_debug("[%d] cancel succeeded\n", tid);
+		      fd__trace("[%d] cancel succeeded\n", tid);
 		    }
 		  else
 		    {
@@ -2506,7 +2504,7 @@ if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid
 		      if (!mpi_flag)
 			continue;	// the send has completed
 
-		      _fd_debug("[%d] previously cancelled store to %d\n",
+		      fd__trace("[%d] previously cancelled store to %d\n",
 				  tid, i);
 		    }
 
@@ -2533,7 +2531,7 @@ if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid
 		{
 		  if (tid != 0)
 		    {
-		      _fd_debug("[%d] ready to leave\n", tid);
+		      fd__trace("[%d] ready to leave\n", tid);
 
 		      _fd_send(0, FD_MSG_READY);
 
@@ -2566,11 +2564,11 @@ if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid
 	      // stop the other processes
 	      if (live_procs > 1)
 		{
-		  _fd_debug("[%d] stopping all processes\n", tid);
+		  fd__trace("[%d] stopping all processes\n", tid);
 		  _fd_broadcast(FD_MSG_DONE, processes, tid);
 		}
 	      else
-		_fd_debug("[%d] all other processes already stopped\n", tid);
+		fd__trace("[%d] all other processes already stopped\n", tid);
 
 	      if (_fd_optimising && status == FD_OK)
 		{
@@ -2581,7 +2579,7 @@ if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid
 		  if (ts.tv_sec)
 		    {
 		      timersub(&ts, &ti, &ts);
-		      _fd_debug("[%d] time to solution was %d.%06ds\n", tid,
+		      fd__trace("[%d] time to solution was %d.%06ds\n", tid,
 				ts.tv_sec, ts.tv_usec);
 		    }
 		}
@@ -2594,7 +2592,7 @@ if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid
 		{
 		  MPI_Request *mpi_request = malloc(sizeof(MPI_Request));
 
-		  _fd_debug("[%d] sending solution\n", tid);
+		  fd__trace("[%d] sending solution\n", tid);
 
 		  if (_fd_optimising)
 		    assert(have_solution);
@@ -2605,7 +2603,7 @@ if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid
 		  if (ts.tv_sec)
 		    {
 		      timersub(&ts, &ti, &ts);
-		      _fd_debug("[%d] time to solution was %d.%06ds\n", tid,
+		      fd__trace("[%d] time to solution was %d.%06ds\n", tid,
 				ts.tv_sec, ts.tv_usec);
 		    }
 
@@ -2613,19 +2611,19 @@ if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid
 		}
 	      else
 		{
-		  _fd_debug("[%d] sending failure notice\n", tid);
+		  fd__trace("[%d] sending failure notice\n", tid);
 		  _fd_send(0, FD_MSG_FAIL);
 		}
 	    }
 
 	  // stop all the agents
-	  _fd_debug("[%d] stopping agents\n", tid);
+	  fd__trace("[%d] stopping agents\n", tid);
 	  for (i = 0; i < started; ++i)
 	    {
 	      int s;
 
 	      if ((s = pthread_cancel(threads[i])) && s != ESRCH)
-		_fd_debug("[%d] error cancelling thread %d = %d\n", tid, i, s);
+		fd__debug("[%d] error cancelling thread %d = %d\n", tid, i, s);
 	    }
 
 	  // make sure all agents have stopped before exiting
@@ -2634,9 +2632,9 @@ if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid
 	      {
 		int s;
 
-		_fd_debug("[%d] joining agent %d\n", tid, i);
+		fd__trace("[%d] joining agent %d\n", tid, i);
 		if ((s = pthread_join(threads[i], NULL)) && s != ESRCH)
-		  _fd_debug("[%d] error joining thread %d = %d\n", tid, i, s);
+		  fd__debug("[%d] error joining thread %d = %d\n", tid, i, s);
 	      }
 
 #if STEAL_WORK >= 2
@@ -2652,13 +2650,13 @@ if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid
 
 #if STEAL_WORK >= 2
 	  // stop all the agents, which are waiting for work
-	  _fd_debug("[%d] stopping agents\n", tid);
+	  fd__trace("[%d] stopping agents\n", tid);
 	  for (i = 0; i < started; ++i)
 	    {
 	      int s;
 
 	      if ((s = pthread_cancel(threads[i])) && s != ESRCH)
-		_fd_debug("[%d] error cancelling thread %d = %d\n", tid, i, s);
+		fd__debug("[%d] error cancelling thread %d = %d\n", tid, i, s);
 	    }
 
 	  // make sure all agents have stopped before exiting
@@ -2666,13 +2664,13 @@ if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid
 	    {
 	      int s;
 
-	      _fd_debug("[%d] joining agent %d\n", tid, i);
+	      fd__trace("[%d] joining agent %d\n", tid, i);
 	      if ((s = pthread_join(threads[i], NULL)) && s != ESRCH)
-		_fd_debug("[%d] error joining thread %d = %d\n", tid, i, s);
+		fd__debug("[%d] error joining thread %d = %d\n", tid, i, s);
 	    }
 
 	  if (!fd_list_empty(saved_stores))
-	    _fd_fatal("saved_stores not empty on exit");
+	    fd__fatal("saved_stores not empty on exit");
 
 	  fd_list_dispose(saved_stores);
 #endif /* STEAL_WORK >= 2 */
@@ -2699,15 +2697,15 @@ if (done) _fd_debug("[%d] done (quitting = %d, ready = %d, timeout = %d)\n", tid
 
   if (_fd_counting_solutions)
     {
-      _fd_output("found %llu solutions\n", total_solutions + external_solutions); // XXX
+      fd__output("found %llu solutions\n", total_solutions + external_solutions); // XXX
 
       status = FD_NOSOLUTION; // XXX
     }
 
   gettimeofday(&to, NULL);
   timersub(&to, &ti, &to);
-#if defined(STATS_PROCS) || !defined(FAST)
-  fd__info("process %d took %d.%06ds\n", tid, to.tv_sec, to.tv_usec);
+#if defined(STATS_PROCS) || defined(TRACE)
+  fd__debug("process %d took %d.%06ds\n", tid, to.tv_sec, to.tv_usec);
 #endif
 
   return status;
diff --git a/src/agents-splitgo.c b/src/agents-splitgo.c
index 3e97a23..c448c8d 100644
--- a/src/agents-splitgo.c
+++ b/src/agents-splitgo.c
@@ -13,6 +13,8 @@
 #include "splitting.h"
 #include "bound.h"
 
+#include "util.h"
+
 #ifndef COMPACT_DOMAINS
 #error "only works with COMPACT_DOMAINS"
 #endif
@@ -81,7 +83,7 @@ int _fd_agent(int n)
       int result;
       struct timeval ti, tis, to, tos;
 
-      _fd_debug("starting agent %d\n", n);
+      fd__trace("starting agent %d\n", n);
 
       gettimeofday(&ti, NULL);
 
@@ -93,11 +95,11 @@ int _fd_agent(int n)
 
       gettimeofday(&to, NULL);
       timersub(&to, &ti, &to);
-      _fd_debug("agent %d took %d.%06ds\n", n, to.tv_sec, to.tv_usec);
+      fd__trace("agent %d took %d.%06ds\n", n, to.tv_sec, to.tv_usec);
 
       while (result == FD_OK)
 	{
-	  _fd_debug("agent %d was successful\n", n);
+	  fd__trace("agent %d was successful\n", n);
 
 	  // ensure that only one agent signals its success
 	  // XXX: only good (?) if only looking for the 1st solution
@@ -106,7 +108,7 @@ int _fd_agent(int n)
 	  // in sem_wait()
 	  pthread_cleanup_push((void (*)(void *)) pthread_mutex_unlock,
 			       &success_mutex);
-	  _fd_debug("agent %d acquired success mutex\n", n);
+	  fd__trace("agent %d acquired success mutex\n", n);
 
 	  // tell the main thread that this agent is done
 	  if (sem_wait(&ready_semaphore))
@@ -119,14 +121,14 @@ int _fd_agent(int n)
 
 	  pthread_mutex_unlock(&success_mutex);
 	  pthread_cleanup_pop(0);
-	  _fd_debug("agent %d released success mutex\n", n);
+	  fd__trace("agent %d released success mutex\n", n);
 
 	  if (!_fd_optimising)
 	    return result;
 
 	  if (sem_wait(&resume_semaphore))
 	    perror("sem_post (resume)");
-	  _fd_debug("[%d] resuming, new bound is %d\n", n, _fd_bound_value());
+	  fd__trace("[%d] resuming, new bound is %d\n", n, _fd_bound_value());
 
 	  // find the next solution
 	  gettimeofday(&tis, NULL);
@@ -136,12 +138,12 @@ int _fd_agent(int n)
 	  gettimeofday(&to, NULL);
 	  timersub(&to, &tis, &tos);
 	  timersub(&to, &ti, &to);
-	  _fd_debug("agent %d took %d.%06ds (%d.%06ds)\n", n,
+	  fd__trace("agent %d took %d.%06ds (%d.%06ds)\n", n,
 		    tos.tv_sec, tos.tv_usec, to.tv_sec, to.tv_usec);
 	}
 
       // eventually, the agent will fail to find a solution
-      _fd_debug("agent %d was unsuccessful\n", n);
+      fd__trace("agent %d was unsuccessful\n", n);
 
       // tell the main thread that this agent is done
       if (sem_wait(&ready_semaphore))
@@ -159,7 +161,7 @@ int _fd_agent(int n)
       unsigned long long solutions;
       struct timeval ti, to;
 
-      _fd_debug("starting agent %d\n", n);
+      fd__trace("starting agent %d\n", n);
 
       gettimeofday(&ti, NULL);
 
@@ -171,9 +173,9 @@ int _fd_agent(int n)
 
       gettimeofday(&to, NULL);
       timersub(&to, &ti, &to);
-      _fd_debug("agent %d took %d.%06ds\n", n, to.tv_sec, to.tv_usec);
+      fd__trace("agent %d took %d.%06ds\n", n, to.tv_sec, to.tv_usec);
 
-      _fd_debug("agent %d found %llu solutions\n", n, solutions);
+      fd__trace("agent %d found %llu solutions\n", n, solutions);
 
       // tell the main thread that this agent is done
       if (sem_wait(&ready_semaphore))
@@ -193,7 +195,7 @@ int _fd_agent(int n)
 
 void _fd_search_space_sizes(_fd_store stores[], int n)
 {
-#ifndef FAST
+#ifdef TRACE
   double s;
   int i, v;
 
@@ -208,9 +210,9 @@ void _fd_search_space_sizes(_fd_store stores[], int n)
       for (v = 0; v < fd__label_vars_count; ++v)
 	s *= _fd_val_size(SVALUE(stores[i][fd__label_vars[v]->index]));
 
-      _fd_debug("search space %d size is %g\n", i, s);
+      fd__trace("search space %d size is %g\n", i, s);
     }
-#endif /* FAST */
+#endif /* TRACE */
 }
 
 int _fd_dsolve()
@@ -265,7 +267,7 @@ int _fd_dsolve()
 
   if (parts < nagents)
     {
-      _fd_debug("reducing to %d agents!\n", parts);
+      fd__trace("reducing to %d agents!\n", parts);
 
       nagents = parts;
     }
@@ -274,7 +276,7 @@ int _fd_dsolve()
 
   gettimeofday(&to, NULL);
   timersub(&to, &ti, &to);
-  _fd_debug("setup took %d.%06ds\n", to.tv_sec, to.tv_usec);
+  fd__trace("setup took %d.%06ds\n", to.tv_sec, to.tv_usec);
 
 #if 0
   for (i = 0; i < nagents; ++i)
@@ -316,7 +318,7 @@ int _fd_dsolve()
 		}
 	    }
 
-	  _fd_debug("main thread waiting\n");
+	  fd__trace("main thread waiting\n");
 
 	  running = started;
 	  do
@@ -329,7 +331,7 @@ int _fd_dsolve()
 	      if (sem_post(&ready_semaphore))
 		perror("sem_post");
 
-	      _fd_debug("got an answer from %s%d\n", solver == -1 ? "-" : "",
+	      fd__trace("got an answer from %s%d\n", solver == -1 ? "-" : "",
 			solver + (solver < 0));
 
 	      if (_fd_optimising && solver >= 0)
@@ -344,10 +346,10 @@ int _fd_dsolve()
 		      memcpy(store, _fd_agents_stores[solver],
 			     fd_variables_count * sizeof(*store));
 
-		      _fd_debug("releasing %d\n", solver);
+		      fd__trace("releasing %d\n", solver);
 		    }
 		  else
-		    _fd_debug("invalid solution, releasing %d\n", solver);
+		    fd__trace("invalid solution, releasing %d\n", solver);
 
 		  if (sem_post(&resume_semaphore))
 		    perror("sem_post (resume)");
@@ -359,14 +361,14 @@ int _fd_dsolve()
 	    }
 	  while ((_fd_optimising || solver < 0) && running > 0);
 
-	  _fd_debug("%s solution\n", solver < 0 ? "found no" : "got a");
+	  fd__trace("%s solution\n", solver < 0 ? "found no" : "got a");
 
 	  for (i = 0; i < started; ++i)
 	    {
 	      int s;
 
 	      if ((s = pthread_cancel(threads[i])) && s != ESRCH)
-		_fd_debug("error cancelling thread %d = %d\n", i, s);
+		fd__debug("error cancelling thread %d = %d\n", i, s);
 	    }
 
 	  // make sure all agents have stopped before exiting
@@ -376,24 +378,26 @@ int _fd_dsolve()
 	      int s;
 
 	      if ((s = pthread_join(threads[i], NULL)) && s != ESRCH)
-		_fd_debug("error joining thread %d = %d\n", i, s);
+		fd__debug("error joining thread %d = %d\n", i, s);
 	    }
 
 	  if (_fd_optimising)
 	    {
 	      if (have_solution >= 0)
 		{
-		  _fd_debug("using agent %d's results\n", have_solution);
+		  fd__trace("using agent %d's results\n", have_solution);
 
+#if defined(STATS_TIME_SOLUTION) || defined(TRACE)
 		  timersub(&ts, &ti, &ts);
-		  _fd_debug("time to solution was %d.%06ds\n", ts.tv_sec, ts.tv_usec);
+		  fd__debug("time to solution was %d.%06ds\n", ts.tv_sec, ts.tv_usec);
+#endif
 
 		  status = FD_OK;
 		}
 	    }
 	  else if (solver >= 0)
 	    {
-	      _fd_debug("using agent %d's results\n", solver);
+	      fd__trace("using agent %d's results\n", solver);
 
 	      status = FD_OK;
 
@@ -404,7 +408,7 @@ int _fd_dsolve()
       else /* agents == 0 */
 	{
 	  if (_fd_optimising)
-	    _fd_fatal("need at least one agent when optimising");
+	    fd__fatal("need at least one agent when optimising");
 
 	  _fd_agents_stores[0] = store;
 
@@ -439,7 +443,7 @@ int _fd_dsolve()
 	      started++;
 	    }
 
-	  _fd_debug("main thread waiting\n");
+	  fd__trace("main thread waiting\n");
 
 	  running = started;
 	  do
@@ -465,7 +469,7 @@ int _fd_dsolve()
 
       _fd_statistics_steal();
 
-      _fd_output("found %llu solutions\n", total_solutions); // XXX
+      fd__output("found %llu solutions\n", total_solutions); // XXX
 
       return FD_NOSOLUTION; // XXX
     }
diff --git a/src/bound.c b/src/bound.c
index db7b757..5fe52c7 100644
--- a/src/bound.c
+++ b/src/bound.c
@@ -5,8 +5,10 @@
 
 #include "fdc_int.h"
 #include "store.h"
-
 #include "bound.h"
+#include "constraints.h"
+
+#include "util.h"
 
 bool _fd_optimising = false;
 
@@ -32,10 +34,10 @@ _fd_bound_data *_fd_init_bound(int n, C_VAR_T v, fd_constraint c,
   _fd_bound_data *b;
 
   if (_fd_bound)
-    _fd_fatal("trying to set a 2nd optimisation constraint");
+    fd__fatal("trying to set a 2nd optimisation constraint");
 
   if ((b = malloc(sizeof(*_fd_bound))) == NULL)
-    _fd_fatal("could not allocate memory for the optimisation bound");
+    fd__fatal("could not allocate memory for the optimisation bound");
 
   b->value = n;
   b->variable = v;
@@ -121,9 +123,7 @@ int fd__bound_and_revise()
 
       if (_fd_revise_wrt_variable(_fd_bound_variable()) == FD_NOSOLUTION)
 	{
-#ifdef CONSTRAINT_TEMPS
 	  fd__constraint_data_reset();
-#endif
 
 	  return FD_NOSOLUTION;
 	}
diff --git a/src/constraints.c b/src/constraints.c
index b7a487d..e178455 100644
--- a/src/constraints.c
+++ b/src/constraints.c
@@ -7,31 +7,46 @@
 #include "variables.h"
 #include "constraints.h"
 
+#include "util.h"
+
 #define MAX_CONSTRAINTS (512 * 512)
 
 int _fd_constraint_count = 0;
 fd_constraint _fd_constraints[MAX_CONSTRAINTS];
 
-#ifdef CONSTRAINT_TEMPS
+
+#define CONSTRAINT_INITIALISATION(name, code) \
+  static void fd_ ## name ## _init(_fd_constraint_class data[]) \
+  { \
+    data[code].propagator2 = fd_ ## name ## _propagate2; \
+    data[code].filter = fd_ ## name ## _filter; \
+  }
+
+#define initialise_constraint(name) fd_ ## name ## _init(_fd_constraint_data)
+
+
+#ifdef CONSTRAINTS_HAVE_STATE
 #define CSTR_DATA_VALID	0x01
 #define CSTR_ENTAILED	0x02
 
 static __thread uint8_t constraint_state_info[MAX_CONSTRAINTS];
+#  ifdef CONSTRAINT_TEMPS
 static __thread void *constraint_memory[MAX_CONSTRAINTS];
-#endif
+#  endif
+#endif /* CONSTRAINTS_HAVE_STATE */
 
 /* generic functions */
 
-fd_constraint _fd_constraint_new(int nvariables, int nconstants)
+fd_constraint fd__constraint_new(int nvariables, int nconstants)
 {
   fd_constraint c;
 
   if (_fd_constraint_count == MAX_CONSTRAINTS)
-    _fd_fatal("too many constraints, increase MAX_CONSTRAINTS");
+    fd__fatal("too many constraints, increase MAX_CONSTRAINTS");
 
   if (c = malloc(sizeof(struct fd_constraint)))
     {
-#ifdef CONSTRAINT_TEMPS
+#if defined(CONSTRAINT_TEMPS) || !defined(DISABLE_ENTAILED)
       c->index = _fd_constraint_count;
 #endif
       c->variables = calloc(nvariables, sizeof(C_VAR_T)); // XXX: check for NULL
@@ -41,12 +56,7 @@ fd_constraint _fd_constraint_new(int nvariables, int nconstants)
       else
 	c->constants = 0;
       c->nconstants = nconstants;
-#ifdef CONSTRAINT_CLASS
       c->kind = -1;	// XXX
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = _fd_undefined;
-      c->propagator = 0;
-#endif /* CONSTRAINT_CLASS */
 
       _fd_constraints[_fd_constraint_count++] = c;
     }
@@ -56,7 +66,7 @@ fd_constraint _fd_constraint_new(int nvariables, int nconstants)
 
 int _fd_undefined()
 {
-  _fd_fatal("called an undefined function");
+  fd__fatal("called an undefined function");
 }
 
 #ifdef DISTRIBUTED_SOLVER
@@ -77,9 +87,17 @@ void _fd_import_constraints(fd_int variables[])
 #endif /* DISTRIBUTED_SOLVER */
 
 
-#ifdef CONSTRAINT_TEMPS
+#ifdef CONSTRAINTS_HAVE_STATE
 #include <string.h>
 
+void fd__constraint_data_reset(void)
+{
+  memset(constraint_state_info, 0,
+	 _fd_constraint_count * sizeof(*constraint_state_info));
+}
+#endif /* CONSTRAINTS_HAVE_STATE */
+
+#ifdef CONSTRAINT_TEMPS
 int fd__constraint_data_valid(fd_constraint constraint)
 {
   return constraint_state_info[constraint->index] & CSTR_DATA_VALID;
@@ -90,6 +108,16 @@ void fd__constraint_remember(fd_constraint constraint)
   constraint_state_info[constraint->index] |= CSTR_DATA_VALID;
 }
 
+void fd__constraint_free_memory(void)
+{
+  int i;
+
+  for (i = 0; i < _fd_constraint_count; ++i)
+    if (constraint_memory[i])
+      free(constraint_memory[i]);
+}
+#endif /* CONSTRAINT_TEMPS */
+
 #ifndef DISABLE_ENTAILED
 void fd__constraint_set_entailed(fd_constraint constraint)
 {
@@ -100,27 +128,12 @@ int fd__constraint_entailed(fd_constraint constraint)
 {
   return constraint_state_info[constraint->index] & CSTR_ENTAILED;
 }
-#else
+#else /* DISABLE_ENTAILED */
 #define fd__constraint_set_entailed(_) ((void) 0)
-#endif
-
-void fd__constraint_data_reset()
-{
-  memset(constraint_state_info, 0,
-	 _fd_constraint_count * sizeof(*constraint_state_info));
-}
+#endif /* DISABLE_ENTAILED */
 
-void fd__constraint_free_memory()
-{
-  int i;
 
-  for (i = 0; i < _fd_constraint_count; ++i)
-    if (constraint_memory[i])
-      free(constraint_memory[i]);
-}
-#else /* CONSTRAINT_TEMPS */
-#define fd__constraint_set_entailed(_) ((void) 0)
-#endif /* CONSTRAINT_TEMPS */
+/* Import the constraints' code. */
 
 #include <constraints/lt.c>
 #include <constraints/le.c>
@@ -152,40 +165,45 @@ void fd__constraint_free_memory()
 #include <constraints/poly-eq-k.c>
 #include <constraints/poly-ne.c>
 #include <constraints/poly-ne-k.c>
+#include <constraints/poly-le-k.c>
 
-#ifdef CONSTRAINT_CLASS
 
 _fd_constraint_class _fd_constraint_data[FD_CONSTR_KINDS];
 
-_FD_CONSTRAINT_INITIALISATION(ne, FD_CONSTR_NE)
-_FD_CONSTRAINT_INITIALISATION(eq, FD_CONSTR_EQ)
-_FD_CONSTRAINT_INITIALISATION(lt, FD_CONSTR_LT)
-_FD_CONSTRAINT_INITIALISATION(le, FD_CONSTR_LE)
-_FD_CONSTRAINT_INITIALISATION(minus_ne, FD_CONSTR_MINUS_NE)
-_FD_CONSTRAINT_INITIALISATION(minus_eq, FD_CONSTR_MINUS_EQ)
-// _FD_CONSTRAINT_INITIALISATION(plus_gt, FD_CONSTR_PLUS_GT)
-_FD_CONSTRAINT_INITIALISATION(var_eq_minus, FD_CONSTR_VAR_EQ_MINUS)
-_FD_CONSTRAINT_INITIALISATION(all_different, FD_CONSTR_ALL_DIFFERENT)
-// _FD_CONSTRAINT_INITIALISATION(exactly_one, FD_CONSTR_EXACTLY_ONE)
-// _FD_CONSTRAINT_INITIALISATION(nogoods, FD_CONSTR_NOGOODS)
-_FD_CONSTRAINT_INITIALISATION(exactly, FD_CONSTR_EXACTLY)
-_FD_CONSTRAINT_INITIALISATION(exactly_var, FD_CONSTR_EXACTLY_VAR)
-_FD_CONSTRAINT_INITIALISATION(sum, FD_CONSTR_SUM)
-_FD_CONSTRAINT_INITIALISATION(var_eq_times, FD_CONSTR_VAR_EQ_TIMES)
-_FD_CONSTRAINT_INITIALISATION(sum_prod, FD_CONSTR_SUM_PROD)
-_FD_CONSTRAINT_INITIALISATION(element, FD_CONSTR_ELEMENT)
-_FD_CONSTRAINT_INITIALISATION(knapsack2, FD_CONSTR_KNAPSACK2)
-_FD_CONSTRAINT_INITIALISATION(sum2, FD_CONSTR_SUM2)
-_FD_CONSTRAINT_INITIALISATION(min, FD_CONSTR_MIN)
-_FD_CONSTRAINT_INITIALISATION(max, FD_CONSTR_MAX)
-_FD_CONSTRAINT_INITIALISATION(poly_eq, FD_CONSTR_POLY_EQ)
-_FD_CONSTRAINT_INITIALISATION(element_var, FD_CONSTR_ELEMENT_VAR)
-_FD_CONSTRAINT_INITIALISATION(exactly_vars, FD_CONSTR_EXACTLY_VARS)
-_FD_CONSTRAINT_INITIALISATION(poly_eq_k, FD_CONSTR_POLY_EQ_K)
-_FD_CONSTRAINT_INITIALISATION(poly_ne, FD_CONSTR_POLY_NE)
-_FD_CONSTRAINT_INITIALISATION(poly_ne_k, FD_CONSTR_POLY_NE_K)
-
-void _fd_init_constraints()
+
+/* Define the constraints' initialisation functions. */
+
+CONSTRAINT_INITIALISATION(ne, FD_CONSTR_NE)
+CONSTRAINT_INITIALISATION(eq, FD_CONSTR_EQ)
+CONSTRAINT_INITIALISATION(lt, FD_CONSTR_LT)
+CONSTRAINT_INITIALISATION(le, FD_CONSTR_LE)
+CONSTRAINT_INITIALISATION(minus_ne, FD_CONSTR_MINUS_NE)
+CONSTRAINT_INITIALISATION(minus_eq, FD_CONSTR_MINUS_EQ)
+// CONSTRAINT_INITIALISATION(plus_gt, FD_CONSTR_PLUS_GT)
+CONSTRAINT_INITIALISATION(var_eq_minus, FD_CONSTR_VAR_EQ_MINUS)
+CONSTRAINT_INITIALISATION(all_different, FD_CONSTR_ALL_DIFFERENT)
+// CONSTRAINT_INITIALISATION(exactly_one, FD_CONSTR_EXACTLY_ONE)
+// CONSTRAINT_INITIALISATION(nogoods, FD_CONSTR_NOGOODS)
+CONSTRAINT_INITIALISATION(exactly, FD_CONSTR_EXACTLY)
+CONSTRAINT_INITIALISATION(exactly_var, FD_CONSTR_EXACTLY_VAR)
+CONSTRAINT_INITIALISATION(sum, FD_CONSTR_SUM)
+CONSTRAINT_INITIALISATION(var_eq_times, FD_CONSTR_VAR_EQ_TIMES)
+CONSTRAINT_INITIALISATION(sum_prod, FD_CONSTR_SUM_PROD)
+CONSTRAINT_INITIALISATION(element, FD_CONSTR_ELEMENT)
+CONSTRAINT_INITIALISATION(knapsack2, FD_CONSTR_KNAPSACK2)
+CONSTRAINT_INITIALISATION(sum2, FD_CONSTR_SUM2)
+CONSTRAINT_INITIALISATION(min, FD_CONSTR_MIN)
+CONSTRAINT_INITIALISATION(max, FD_CONSTR_MAX)
+CONSTRAINT_INITIALISATION(poly_eq, FD_CONSTR_POLY_EQ)
+CONSTRAINT_INITIALISATION(element_var, FD_CONSTR_ELEMENT_VAR)
+CONSTRAINT_INITIALISATION(exactly_vars, FD_CONSTR_EXACTLY_VARS)
+CONSTRAINT_INITIALISATION(poly_eq_k, FD_CONSTR_POLY_EQ_K)
+CONSTRAINT_INITIALISATION(poly_ne, FD_CONSTR_POLY_NE)
+CONSTRAINT_INITIALISATION(poly_ne_k, FD_CONSTR_POLY_NE_K)
+CONSTRAINT_INITIALISATION(poly_le_k, FD_CONSTR_POLY_LE_K)
+
+/* Initialises the constraints. */
+void fd__init_constraints()
 {
   static int done = 0;
 
@@ -194,35 +212,34 @@ void _fd_init_constraints()
 
   memset(_fd_constraint_data, 0, sizeof(_fd_constraint_data)); // XXX?
 
-  _fd_init_constraint(ne);
-  _fd_init_constraint(eq);
-  _fd_init_constraint(lt);
-  _fd_init_constraint(le);
-  _fd_init_constraint(minus_ne);
-  _fd_init_constraint(minus_eq);
-//  _fd_init_constraint(plus_gt);
-  _fd_init_constraint(var_eq_minus);
-  _fd_init_constraint(all_different);
-//  _fd_init_constraint(exactly_one);
-//  _fd_init_constraint(nogoods);
-  _fd_init_constraint(exactly);
-  _fd_init_constraint(exactly_var);
-  _fd_init_constraint(sum);
-  _fd_init_constraint(var_eq_times);
-  _fd_init_constraint(sum_prod);
-  _fd_init_constraint(element);
-  _fd_init_constraint(knapsack2);
-  _fd_init_constraint(sum2);
-  _fd_init_constraint(min);
-  _fd_init_constraint(max);
-  _fd_init_constraint(poly_eq);
-  _fd_init_constraint(element_var);
-  _fd_init_constraint(exactly_vars);
-  _fd_init_constraint(poly_eq_k);
-  _fd_init_constraint(poly_ne);
-  _fd_init_constraint(poly_ne_k);
+  initialise_constraint(ne);
+  initialise_constraint(eq);
+  initialise_constraint(lt);
+  initialise_constraint(le);
+  initialise_constraint(minus_ne);
+  initialise_constraint(minus_eq);
+//  initialise_constraint(plus_gt);
+  initialise_constraint(var_eq_minus);
+  initialise_constraint(all_different);
+//  initialise_constraint(exactly_one);
+//  initialise_constraint(nogoods);
+  initialise_constraint(exactly);
+  initialise_constraint(exactly_var);
+  initialise_constraint(sum);
+  initialise_constraint(var_eq_times);
+  initialise_constraint(sum_prod);
+  initialise_constraint(element);
+  initialise_constraint(knapsack2);
+  initialise_constraint(sum2);
+  initialise_constraint(min);
+  initialise_constraint(max);
+  initialise_constraint(poly_eq);
+  initialise_constraint(element_var);
+  initialise_constraint(exactly_vars);
+  initialise_constraint(poly_eq_k);
+  initialise_constraint(poly_ne);
+  initialise_constraint(poly_ne_k);
+  initialise_constraint(poly_le_k);
 
   done = 1;
 }
-
-#endif /* CONSTRAINT_CLASS */
diff --git a/src/constraints.h b/src/constraints.h
index 0061390..5ba059f 100644
--- a/src/constraints.h
+++ b/src/constraints.h
@@ -4,8 +4,6 @@ extern fd_constraint _fd_constraints[];
 
 #define CONSTRAINT(v,c) _fd_constraints[(v)->constraints[c]]
 
-#ifdef CONSTRAINT_CLASS
-
 typedef enum {
   FD_CONSTR_NE = 0,
   FD_CONSTR_EQ,
@@ -36,37 +34,42 @@ typedef enum {
   FD_CONSTR_POLY_EQ_K,
   FD_CONSTR_POLY_NE,
   FD_CONSTR_POLY_NE_K,
+  FD_CONSTR_POLY_LE_K,
   FD_CONSTR_KINDS		// number of kinds of constraints
 } _fd_constraint_kind;
 
 typedef struct _fd_constraint_class {
   int (*propagator2)(fd_constraint, fd_int);
+			  // propagates changes in a variable's
+			  // domain; 2nd argument is the variable
+			  // against which to revise the domains of
+			  // the other variables in the constraint;
+			  // returns FD_NOSOLUTION if some domain
+			  // becomes empty, and FD_OK otherwise
   int (*filter)(fd_constraint);
+			  // performs an initial filtering of the
+			  // domains of the constraint variables
 } _fd_constraint_class;
 
-extern _fd_constraint_class _fd_constraint_data[FD_CONSTR_KINDS];
-
-
-#define _FD_CONSTRAINT_INITIALISATION(name, code) \
-  static void _fd_ ## name ## _init(_fd_constraint_class data[]) \
-  { \
-    data[code].propagator2 = fd_ ## name ## _propagate2; \
-    data[code].filter = fd_ ## name ## _filter; \
-  }
-
-#define _fd_init_constraint(name) _fd_ ## name ## _init(_fd_constraint_data)
+extern _fd_constraint_class _fd_constraint_data[];
 
 #define _fd_propagate(c,v) (_fd_constraint_data[(c)->kind].propagator2(c, v))
 #define _fd_filter(c) (_fd_constraint_data[(c)->kind].filter(c))
 
-#else /* CONSTRAINT_CLASS */
+void fd__init_constraints();
 
-#define _fd_propagate(c,v) ((c)->propagator2(c, v))
-#define _fd_filter(c) ((c)->filter(c))
 
-#endif /* CONSTRAINT_CLASS */
+#if defined(CONSTRAINT_TEMPS) || !defined(DISABLE_ENTAILED)
+#define CONSTRAINTS_HAVE_STATE 1
+#endif
+
+#ifdef CONSTRAINTS_HAVE_STATE
+void fd__constraint_data_reset(void);
+#else
+#define fd__constraint_data_reset() ((void) 0)
+#endif
 
-#if defined(CONSTRAINT_TEMPS) && !defined(DISABLE_ENTAILED)
+#ifndef DISABLE_ENTAILED
 int fd__constraint_entailed(fd_constraint);
 #else
 #define fd__constraint_entailed(_) 0
diff --git a/src/constraints/all-different.c b/src/constraints/all-different.c
index b2ad0a5..4e95ca9 100644
--- a/src/constraints/all-different.c
+++ b/src/constraints/all-different.c
@@ -128,7 +128,7 @@ static int fd_all_different_propagate(fd_constraint this, fd_int revise)
 #if defined(USE_ENTAILED) && 0
   // XXX: hampers performance when not optimised
   if (set == this->nvariables - 1)
-    _fd_constraint_set_entailed(this);
+    fd__constraint_set_entailed(this);
 #endif
 
   if (changed)
@@ -148,18 +148,14 @@ fd_constraint fd_all_different(fd_int *variables, int nvariables)
   if (nvariables == 2)
     return fd_ne(variables[0], variables[1]);
 
-  c = _fd_constraint_new(nvariables, 0);
+  c = fd__constraint_new(nvariables, 0);
 
   if (c)
     {
       for (i = 0; i < nvariables; ++i)
 	c->variables[i] = FD_INT2C_VAR(variables[i]);
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_ALL_DIFFERENT;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_all_different_propagate2;
-      c->propagator = fd_all_different_propagate;
-#endif /* CONSTRAINT_CLASS */
 
       for (i = 0; i < nvariables; ++i)
 	_fd_var_add_constraint(VAR(c, i), c);
diff --git a/src/constraints/element-var.c b/src/constraints/element-var.c
index 3f73946..448f3c6 100644
--- a/src/constraints/element-var.c
+++ b/src/constraints/element-var.c
@@ -143,7 +143,7 @@ static int fd_element_var_propagate2(fd_constraint this, fd_int culprit)
 fd_constraint fd_element_var(fd_int *variables, int nvariables, fd_int index,
 			     fd_int value)
 {
-  fd_constraint c = _fd_constraint_new(nvariables + 2, 0);
+  fd_constraint c = fd__constraint_new(nvariables + 2, 0);
   int i;
 
   // XXX: add a constraint 0 <= INDEX < nvariables
@@ -154,11 +154,8 @@ fd_constraint fd_element_var(fd_int *variables, int nvariables, fd_int index,
 	c->variables[i] = FD_INT2C_VAR(variables[i]);
       c->variables[nvariables] = FD_INT2C_VAR(index);
       c->variables[nvariables + 1] = FD_INT2C_VAR(value);
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_ELEMENT_VAR;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_element_var_propagate2;
-#endif /* CONSTRAINT_CLASS */
 
       for (i = 0; i < c->nvariables; ++i)
 	_fd_var_add_constraint(VAR(c, i), c);
diff --git a/src/constraints/element.c b/src/constraints/element.c
index 1318a2f..dff7929 100644
--- a/src/constraints/element.c
+++ b/src/constraints/element.c
@@ -102,7 +102,7 @@ static int fd_element_propagate2(fd_constraint this, fd_int culprit)
 
 fd_constraint fd_element(fd_int *variables, int nvariables, fd_int index, int k)
 {
-  fd_constraint c = _fd_constraint_new(nvariables + 1, 1);
+  fd_constraint c = fd__constraint_new(nvariables + 1, 1);
   int i;
 
   // XXX: add a constraint 0 <= INDEX < nvariables
@@ -113,11 +113,8 @@ fd_constraint fd_element(fd_int *variables, int nvariables, fd_int index, int k)
 	c->variables[i] = FD_INT2C_VAR(variables[i]);
       c->variables[nvariables] = FD_INT2C_VAR(index);
       c->constants[0] = k;
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_ELEMENT;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_element_propagate2;
-#endif /* CONSTRAINT_CLASS */
 
       for (i = 0; i < c->nvariables; ++i)
 	_fd_var_add_constraint(VAR(c, i), c);
diff --git a/src/constraints/eq.c b/src/constraints/eq.c
index 300092b..ce01d27 100644
--- a/src/constraints/eq.c
+++ b/src/constraints/eq.c
@@ -62,17 +62,13 @@ int fd_eq_propagate(fd_constraint this, fd_int revise)
 
 fd_constraint fd_eq(fd_int x, fd_int y)
 {
-  fd_constraint c = _fd_constraint_new(2, 0);
+  fd_constraint c = fd__constraint_new(2, 0);
 
   if (c)
     {
       c->variables[0] = FD_INT2C_VAR(x); c->variables[1] = FD_INT2C_VAR(y);
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_EQ;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_eq_propagate2;
-      c->propagator = fd_eq_propagate;
-#endif /* CONSTRAINT_CLASS */
 
       _fd_var_add_constraint(x, c);
       _fd_var_add_constraint(y, c);
diff --git a/src/constraints/exactly-one.c b/src/constraints/exactly-one.c
index 99642e4..e15dbd2 100644
--- a/src/constraints/exactly-one.c
+++ b/src/constraints/exactly-one.c
@@ -72,7 +72,7 @@ static int fd_exactly_one_propagate(fd_constraint this, fd_int revise)
 
 fd_constraint fd_exactly_one(fd_int *variables, int nvariables, int value)
 {
-  fd_constraint c = _fd_constraint_new(nvariables, 1);
+  fd_constraint c = fd__constraint_new(nvariables, 1);
   int i;
 
   if (c)
@@ -80,11 +80,8 @@ fd_constraint fd_exactly_one(fd_int *variables, int nvariables, int value)
       for (i = 0; i < nvariables; ++i)
 	c->variables[i] = FD_INT2C_VAR(variables[i]);
       c->constants[0] = value;
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_EXACTLY_ONE;
-#else /* CONSTRAINT_CLASS */
-      c->propagator = fd_exactly_one_propagate;
-#endif /* CONSTRAINT_CLASS */
 
       for (i = 0; i < nvariables; ++i)
 	_fd_var_add_constraint(VAR(c, i), c);
diff --git a/src/constraints/exactly-var.c b/src/constraints/exactly-var.c
index 11e764d..82df8bf 100644
--- a/src/constraints/exactly-var.c
+++ b/src/constraints/exactly-var.c
@@ -159,7 +159,7 @@ static int fd_exactly_var_propagate2(fd_constraint this, fd_int culprit)
 
 fd_constraint fd_exactly_var(fd_int *variables, int nvariables, fd_int card, int k)
 {
-  fd_constraint c = _fd_constraint_new(nvariables + 1, 1);
+  fd_constraint c = fd__constraint_new(nvariables + 1, 1);
   int i;
 
   if (c)
@@ -168,11 +168,8 @@ fd_constraint fd_exactly_var(fd_int *variables, int nvariables, fd_int card, int
 	c->variables[i] = FD_INT2C_VAR(variables[i]);
       c->variables[nvariables] = FD_INT2C_VAR(card);
       c->constants[0] = k;
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_EXACTLY_VAR;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_exactly_var_propagate2;
-#endif /* CONSTRAINT_CLASS */
 
       for (i = 0; i <= nvariables; ++i)
 	_fd_var_add_constraint(VAR(c, i), c);
diff --git a/src/constraints/exactly-vars.c b/src/constraints/exactly-vars.c
index 0ba45d1..8f5ee1f 100644
--- a/src/constraints/exactly-vars.c
+++ b/src/constraints/exactly-vars.c
@@ -166,7 +166,7 @@ static int fd_exactly_vars_propagate2(fd_constraint this, fd_int culprit)
 
 fd_constraint fd_exactly_vars(fd_int *variables, int nvariables, fd_int card, fd_int k)
 {
-  fd_constraint c = _fd_constraint_new(nvariables + 2, 0);
+  fd_constraint c = fd__constraint_new(nvariables + 2, 0);
   int i;
 
   if (c)
@@ -175,11 +175,8 @@ fd_constraint fd_exactly_vars(fd_int *variables, int nvariables, fd_int card, fd
 	c->variables[i] = FD_INT2C_VAR(variables[i]);
       c->variables[nvariables] = FD_INT2C_VAR(card);
       c->variables[nvariables + 1] = FD_INT2C_VAR(k);
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_EXACTLY_VARS;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_exactly_vars_propagate2;
-#endif /* CONSTRAINT_CLASS */
 
       for (i = 0; i < nvariables + 2; ++i)
 	_fd_var_add_constraint(VAR(c, i), c);
diff --git a/src/constraints/exactly.c b/src/constraints/exactly.c
index eed6304..6e84d17 100644
--- a/src/constraints/exactly.c
+++ b/src/constraints/exactly.c
@@ -74,7 +74,7 @@ static int fd_exactly_propagate2(fd_constraint this, fd_int culprit)
 
 fd_constraint fd_exactly(fd_int *variables, int nvariables, int cardinal, int k)
 {
-  fd_constraint c = _fd_constraint_new(nvariables, 2);
+  fd_constraint c = fd__constraint_new(nvariables, 2);
   int i;
 
   if (c)
@@ -83,11 +83,8 @@ fd_constraint fd_exactly(fd_int *variables, int nvariables, int cardinal, int k)
 	c->variables[i] = FD_INT2C_VAR(variables[i]);
       c->constants[0] = k;
       c->constants[1] = cardinal;
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_EXACTLY;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_exactly_propagate2;
-#endif /* CONSTRAINT_CLASS */
 
       for (i = 0; i < nvariables; ++i)
 	_fd_var_add_constraint(VAR(c, i), c);
diff --git a/src/constraints/knapsack.c b/src/constraints/knapsack.c
index 07c9edf..217ba43 100644
--- a/src/constraints/knapsack.c
+++ b/src/constraints/knapsack.c
@@ -105,7 +105,7 @@ static int fd_knapsack_propagate2(fd_constraint this, fd_int culprit)
 
 fd_constraint fd_knapsack(fd_int variables[], int nvariables, fd_int limits)
 {
-  fd_constraint c = _fd_constraint_new(nvariables + 1, 0);
+  fd_constraint c = fd__constraint_new(nvariables + 1, 0);
   int i;
 
   if (c)
@@ -113,11 +113,8 @@ fd_constraint fd_knapsack(fd_int variables[], int nvariables, fd_int limits)
       for (i = 0; i < nvariables; ++i)
 	c->variables[i] = FD_INT2C_VAR(variables[i]);
       c->variables[nvariables] = FD_INT2C_VAR(limits);
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_KNAPSACK;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_knapsack_propagate2;
-#endif /* CONSTRAINT_CLASS */
 
       for (i = 0; i < c->nvariables; ++i)
 	_fd_var_add_constraint(VAR(c, i), c);
diff --git a/src/constraints/knapsack2.c b/src/constraints/knapsack2.c
index 175eafc..24c8440 100644
--- a/src/constraints/knapsack2.c
+++ b/src/constraints/knapsack2.c
@@ -4,12 +4,12 @@
 
 static int fd_knapsack2_filter(fd_constraint this)
 {
-#ifdef CONSTRAINT_TEMPS
   int ub, lb;
   int min, max;
   int terms = (this->nvariables - 1) / 2;
   int *mins, *maxs;	// XXX: trouble possible if a variable appears repeated
   int i;
+#ifdef CONSTRAINT_TEMPS
   int *base;
 
   assert(!fd__constraint_data_valid(this));
@@ -21,6 +21,10 @@ static int fd_knapsack2_filter(fd_constraint this)
 
   mins = base + 4;
   maxs = mins + 2 * terms;
+#else
+  mins = alloca(2 * terms * sizeof(*mins));
+  maxs = alloca(2 * terms * sizeof(*maxs));
+#endif
 
   lb = _fd_var_min(VAR(this, this->nvariables - 1));	// lower bound
   ub = _fd_var_max(VAR(this, this->nvariables - 1));	// upper bound
@@ -115,6 +119,7 @@ static int fd_knapsack2_filter(fd_constraint this)
       (max < ub && _fd_var_del_gt(max, VAR(this, this->nvariables - 1))))
     _fd_revise_connected(this, VAR(this, this->nvariables - 1));
 
+#ifdef CONSTRAINT_TEMPS
   // save values
   *base = lb;
   *(base + 1) = ub;
@@ -122,120 +127,16 @@ static int fd_knapsack2_filter(fd_constraint this)
   *(base + 3) = max;
 
   fd__constraint_remember(this);
+#endif
 
   return FD_OK;
-#else /* CONSTRAINT_TEMPS */
-  int ub, lb;
-  int min, max;
-  int terms = (this->nvariables - 1) / 2;
-  int *mins, *maxs;	// XXX: trouble possible if a variable appears repeated
-  int i;
-
-  mins = alloca(2 * terms * sizeof(*mins));
-  maxs = alloca(2 * terms * sizeof(*maxs));
-
-  lb = _fd_var_min(VAR(this, this->nvariables - 1));	// lower bound
-  ub = _fd_var_max(VAR(this, this->nvariables - 1));	// upper bound
-
-  // sum the minima of the terms
-  min = 0;
-
-  for (i = 0; i < terms; ++i)
-    {
-      mins[i] = _fd_var_min(VAR(this, i));
-      mins[terms + i] = _fd_var_min(VAR(this, terms + i));
-
-      min += mins[i] * mins[terms + i];
-
-      if (min > ub)
-	return FD_NOSOLUTION;
-    }
-
-  // sum the maxima of the terms
-  max = 0;
-
-  for (i = 0; i < terms; ++i)
-    {
-      maxs[i] = _fd_var_max(VAR(this, i));
-      maxs[terms + i] = _fd_var_max(VAR(this, terms + i));
-
-      max += maxs[i] * maxs[terms + i];
-    }
-
-  if (max < lb)
-    return FD_NOSOLUTION;
-
-  // XXX: poor man's propagation
-  if (min == ub)
-    for (i = 0; i < terms; ++i)
-      {
-	int min_x = mins[i];
-	int min_y = mins[terms + i];
-
-	if (min_x != 0 || min_y != 0)
-	  {
-	    if (min_y != 0 && _fd_var_del_gt(min_x, VAR(this, i)))
-	      _fd_revise_connected(this, VAR(this, i));
-
-	    if (min_x != 0 && _fd_var_del_gt(min_y, VAR(this, terms + i)))
-	      _fd_revise_connected(this, VAR(this, terms + i));
-	  }
-      }
-  else if (max == lb)
-    for (i = 0; i < terms; ++i)
-      {
-	int max_x = maxs[i];
-	int max_y = maxs[terms + i];
-
-	if (max_x != 0 && max_y != 0)
-	  {
-	    if (_fd_var_del_lt(max_x, VAR(this, i)))
-	      _fd_revise_connected(this, VAR(this, i));
-
-	    if (_fd_var_del_lt(max_y, VAR(this, terms + i)))
-	      _fd_revise_connected(this, VAR(this, terms + i));
-	  }
-      }
-  else if (max > ub)
-    for (i = 0; i < terms; ++i)
-      {
-	int min_x = mins[i];
-	int max_x = maxs[i];
-	int min_y = mins[terms + i];
-	int max_y = maxs[terms + i];
-
-	if (min_x * (max_y - min_y) > ub - min)
-	  {
-	    max_y = (ub - min + min_x * min_y) / min_x;
-
-	    _fd_var_del_gt(max_y, VAR(this, terms + i));
-
-	    _fd_revise_connected(this, VAR(this, terms + i));
-	  }
-
-	if ((max_x - min_x) * min_y > ub - min)
-	  {
-	    max_x = (ub - min + min_x * min_y) / min_y;
-
-	    _fd_var_del_gt(max_x, VAR(this, i));
-
-	    _fd_revise_connected(this, VAR(this, i));
-	  }
-      }
-
-  if ((min > lb && _fd_var_del_lt(min, VAR(this, this->nvariables - 1))) |
-      (max < ub && _fd_var_del_gt(max, VAR(this, this->nvariables - 1))))
-    _fd_revise_connected(this, VAR(this, this->nvariables - 1));
-
-  return FD_OK;
-#endif /* CONSTRAINT_TEMPS */
 }
 
 static int fd_knapsack2_propagate2(fd_constraint this, fd_int culprit)
 {
 #ifdef CONSTRAINT_TEMPS
   int ub, lb;
-  unsigned int min, max;
+  int min, max;
   int terms = (this->nvariables - 1) / 2;
   int *mins, *maxs;	// XXX: trouble possible if a variable appears repeated
   int i;
@@ -379,7 +280,7 @@ static int fd_knapsack2_propagate2(fd_constraint this, fd_int culprit)
 
 fd_constraint fd_knapsack2(fd_int xs[], fd_int ys[], int nvariables, fd_int limits)
 {
-  fd_constraint c = _fd_constraint_new(2 * nvariables + 1, 0);
+  fd_constraint c = fd__constraint_new(2 * nvariables + 1, 0);
   int i;
 
   if (c)
@@ -389,11 +290,8 @@ fd_constraint fd_knapsack2(fd_int xs[], fd_int ys[], int nvariables, fd_int limi
       for (; i < 2 * nvariables; ++i)
 	c->variables[i] = FD_INT2C_VAR(ys[i - nvariables]);
       c->variables[2 * nvariables] = FD_INT2C_VAR(limits);
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_KNAPSACK2;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_knapsack2_propagate2;
-#endif /* CONSTRAINT_CLASS */
 
       for (i = 0; i < c->nvariables; ++i)
 	_fd_var_add_constraint(VAR(c, i), c);
diff --git a/src/constraints/le.c b/src/constraints/le.c
index 91778f1..65edf2b 100644
--- a/src/constraints/le.c
+++ b/src/constraints/le.c
@@ -1,4 +1,4 @@
-/* X le Y */
+/* x <= y */
 
 static int fd_le_filter(fd_constraint this)
 {
@@ -7,12 +7,21 @@ static int fd_le_filter(fd_constraint this)
   x = VAR(this, 0);
   y = VAR(this, 1);
 
+#ifndef DISABLE_ENTAILED
+  if (_fd_var_max(x) <= _fd_var_min(y))
+    {
+      fd__constraint_set_entailed(this);
+
+      return FD_OK;
+    }
+#endif
+
   if (_fd_var_del_lt(_fd_var_min(x), y))
     {
       if (fd_domain_empty(y))
 	return FD_NOSOLUTION;
 
-      _fd_revise_connected(NULL, y);
+      _fd_revise_connected(this, y);
     }
 
   if (_fd_var_del_gt(_fd_var_max(y), x))
@@ -20,10 +29,10 @@ static int fd_le_filter(fd_constraint this)
       if (fd_domain_empty(x))
 	return FD_NOSOLUTION;
 
-      _fd_revise_connected(NULL, x);
+      _fd_revise_connected(this, x);
     }
 
-#if CONSTRAINT_TEMPS > 3
+#ifndef DISABLE_ENTAILED
   if (_fd_var_max(x) <= _fd_var_min(y))
     fd__constraint_set_entailed(this);
 #endif
@@ -31,39 +40,63 @@ static int fd_le_filter(fd_constraint this)
   return FD_OK;
 }
 
-int fd_le_propagate2(fd_constraint this, fd_int culprit)
+static int fd_le_propagate2(fd_constraint this, fd_int culprit)
 {
+  fd_int x, y;
+  int vmax, vmin;
   fd_int revise;
   int changed = 0;
 
-  if (culprit == VAR(this, 0))
+  x = VAR(this, 0);
+  y = VAR(this, 1);
+
+#ifndef DISABLE_ENTAILED
+  vmax = _fd_var_max(x);
+  vmin = _fd_var_min(y);
+
+  if (vmax <= vmin)
     {
-      revise = VAR(this, 1);
+      fd__constraint_set_entailed(this);
 
-      changed = _fd_var_del_lt(_fd_var_min(culprit), revise);
+      return FD_OK;
     }
-  else
+#endif
+
+  if (culprit == x)
     {
-      revise = VAR(this, 0);
+      revise = y;
 
-      changed = _fd_var_del_gt(_fd_var_max(culprit), revise);
+      vmin = _fd_var_min(x);
+
+      changed = _fd_var_del_lt(vmin, y);
     }
+  else
+    {
+      revise = x;
 
-  if (changed && fd_domain_empty(revise))
-    return FD_NOSOLUTION;
+      vmax = _fd_var_max(y);
 
-#if CONSTRAINT_TEMPS > 3
-  if (_fd_var_max(VAR(this, 0)) <= _fd_var_min(VAR(this, 1)))
-    fd__constraint_set_entailed(this);
-#endif
+      changed = _fd_var_del_gt(vmax, x);
+    }
 
   if (changed)
-    _fd_revise_connected(0, revise);
+    {
+      if (fd_domain_empty(revise))
+	return FD_NOSOLUTION;
+
+      _fd_revise_connected(this, revise);
+
+#ifndef DISABLE_ENTAILED
+      // see if the culprit's domain is a singleton
+      if (vmin == vmax)
+	fd__constraint_set_entailed(this);
+#endif
+    }
 
   return FD_OK;
 }
 
-int fd_le_propagate(fd_constraint this, fd_int revise)
+static int fd_le_propagate(fd_constraint this, fd_int revise)
 {
   int changed = 0;
 
@@ -77,7 +110,7 @@ int fd_le_propagate(fd_constraint this, fd_int revise)
 
 #ifdef USE_ENTAILED
   if (_fd_var_max(VAR(this, 0)) <= _fd_var_min(VAR(this, 1)))
-    _fd_constraint_set_entailed(this);
+    fd__constraint_set_entailed(this);
 #endif
 
   // XXX: enqueue further updates here?
@@ -89,17 +122,13 @@ int fd_le_propagate(fd_constraint this, fd_int revise)
 
 fd_constraint fd_le(fd_int x, fd_int y)
 {
-  fd_constraint c = _fd_constraint_new(2, 0);
+  fd_constraint c = fd__constraint_new(2, 0);
 
   if (c)
     {
       c->variables[0] = FD_INT2C_VAR(x); c->variables[1] = FD_INT2C_VAR(y);
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_LE;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_le_propagate2;
-      c->propagator = fd_le_propagate;
-#endif /* CONSTRAINT_CLASS */
 
       _fd_var_add_constraint(x, c);
       _fd_var_add_constraint(y, c);
diff --git a/src/constraints/lt.c b/src/constraints/lt.c
index 95675bb..7de09fe 100644
--- a/src/constraints/lt.c
+++ b/src/constraints/lt.c
@@ -1,6 +1,6 @@
-/* X lt Y */
+/* x < y */
 
-int fd_lt_filter(fd_constraint this)
+static int fd_lt_filter(fd_constraint this)
 {
   fd_int x, y;
 
@@ -9,7 +9,11 @@ int fd_lt_filter(fd_constraint this)
 
 #ifndef DISABLE_ENTAILED
   if (_fd_var_max(x) < _fd_var_min(y))
-    fd__constraint_set_entailed(this);
+    {
+      fd__constraint_set_entailed(this);
+
+      return FD_OK;
+    }
 #endif
 
   if (_fd_var_del_le(_fd_var_min(x), y))
@@ -17,7 +21,7 @@ int fd_lt_filter(fd_constraint this)
       if (fd_domain_empty(y))
 	return FD_NOSOLUTION;
 
-      _fd_revise_connected(NULL, y);
+      _fd_revise_connected(this, y);
     }
 
   if (_fd_var_del_ge(_fd_var_max(y), x))
@@ -25,19 +29,32 @@ int fd_lt_filter(fd_constraint this)
       if (fd_domain_empty(x))
 	return FD_NOSOLUTION;
 
-      _fd_revise_connected(NULL, x);
+      _fd_revise_connected(this, x);
     }
 
+#ifndef DISABLE_ENTAILED
+  if (_fd_var_max(x) < _fd_var_min(y))
+    fd__constraint_set_entailed(this);
+#endif
+
   return FD_OK;
 }
 
-int fd_lt_propagate2(fd_constraint this, fd_int culprit)
+static int fd_lt_propagate2(fd_constraint this, fd_int culprit)
 {
+  fd_int x, y;
+  int vmax, vmin;
   fd_int revise;
   int changed = 0;
 
-#if !defined(DISABLE_ENTAILED) && CONSTRAINT_TEMPS > 2
-  if (_fd_var_max(VAR(this, 0)) < _fd_var_min(VAR(this, 1)))
+  x = VAR(this, 0);
+  y = VAR(this, 1);
+
+#ifndef DISABLE_ENTAILED
+  vmax = _fd_var_max(x);
+  vmin = _fd_var_min(y);
+
+  if (vmax < vmin)
     {
       fd__constraint_set_entailed(this);
 
@@ -45,29 +62,41 @@ int fd_lt_propagate2(fd_constraint this, fd_int culprit)
     }
 #endif
 
-  if (culprit == VAR(this, 0))
+  if (culprit == x)
     {
-      revise = VAR(this, 1);
+      revise = y;
 
-      changed = _fd_var_del_le(_fd_var_min(culprit), revise);
+      vmin = _fd_var_min(x);
+
+      changed = _fd_var_del_le(vmin, y);
     }
   else
     {
-      revise = VAR(this, 0);
+      revise = x;
 
-      changed = _fd_var_del_ge(_fd_var_max(culprit), revise);
-    }
+      vmax = _fd_var_max(y);
 
-  if (changed && fd_domain_empty(revise))
-    return FD_NOSOLUTION;
+      changed = _fd_var_del_ge(vmax, x);
+    }
 
   if (changed)
-    _fd_revise_connected(0, revise);
+    {
+      if (fd_domain_empty(revise))
+	return FD_NOSOLUTION;
+
+      _fd_revise_connected(this, revise);
+
+#ifndef DISABLE_ENTAILED
+      // see if the culprit's domain is a singleton
+      if (vmin == vmax)
+	fd__constraint_set_entailed(this);
+#endif
+    }
 
   return FD_OK;
 }
 
-int fd_lt_propagate(fd_constraint this, fd_int revise)
+static int fd_lt_propagate(fd_constraint this, fd_int revise)
 {
   int changed = 0;
 
@@ -81,7 +110,7 @@ int fd_lt_propagate(fd_constraint this, fd_int revise)
 
 #ifdef USE_ENTAILED
   if (_fd_var_max(VAR(this, 0)) < _fd_var_min(VAR(this, 1)))
-    _fd_constraint_set_entailed(this);
+    fd__constraint_set_entailed(this);
 #endif
 
   // XXX: enqueue further updates here?
@@ -93,17 +122,13 @@ int fd_lt_propagate(fd_constraint this, fd_int revise)
 
 fd_constraint fd_lt(fd_int x, fd_int y)
 {
-  fd_constraint c = _fd_constraint_new(2, 0);
+  fd_constraint c = fd__constraint_new(2, 0);
 
   if (c)
     {
       c->variables[0] = FD_INT2C_VAR(x); c->variables[1] = FD_INT2C_VAR(y);
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_LT;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_lt_propagate2;
-      c->propagator = fd_lt_propagate;
-#endif /* CONSTRAINT_CLASS */
 
       _fd_var_add_constraint(x, c);
       _fd_var_add_constraint(y, c);
diff --git a/src/constraints/max.c b/src/constraints/max.c
index 6878345..31e2d05 100644
--- a/src/constraints/max.c
+++ b/src/constraints/max.c
@@ -84,16 +84,13 @@ bool fd_max_bound_variable(fd_constraint this, int bound)
 
 fd_constraint fd_max(fd_int variable)
 {
-  fd_constraint c = _fd_constraint_new(1, 0);
+  fd_constraint c = fd__constraint_new(1, 0);
 
   if (c)
     {
       c->variables[0] = FD_INT2C_VAR(variable);
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_MAX;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_max_propagate2;
-#endif /* CONSTRAINT_CLASS */
 
       _fd_var_add_constraint(variable, c);
 
diff --git a/src/constraints/min.c b/src/constraints/min.c
index a77f813..72d6fdf 100644
--- a/src/constraints/min.c
+++ b/src/constraints/min.c
@@ -84,16 +84,13 @@ bool fd_min_bound_variable(fd_constraint this, int bound)
 
 fd_constraint fd_min(fd_int variable)
 {
-  fd_constraint c = _fd_constraint_new(1, 0);
+  fd_constraint c = fd__constraint_new(1, 0);
 
   if (c)
     {
       c->variables[0] = FD_INT2C_VAR(variable);
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_MIN;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_min_propagate2;
-#endif /* CONSTRAINT_CLASS */
 
       _fd_var_add_constraint(variable, c);
 
diff --git a/src/constraints/minus-eq.c b/src/constraints/minus-eq.c
index 3ae7ed9..572ad78 100644
--- a/src/constraints/minus-eq.c
+++ b/src/constraints/minus-eq.c
@@ -105,18 +105,14 @@ int fd_minus_eq_propagate(fd_constraint this, fd_int revise)
 
 fd_constraint fd_minus_eq(fd_int x, fd_int y, int k)
 {
-  fd_constraint c = _fd_constraint_new(2, 1);
+  fd_constraint c = fd__constraint_new(2, 1);
 
   if (c)
     {
       c->variables[0] = FD_INT2C_VAR(x); c->variables[1] = FD_INT2C_VAR(y);
       c->constants[0] = k;
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_MINUS_EQ;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_minus_eq_propagate2;
-      c->propagator = fd_minus_eq_propagate;
-#endif /* CONSTRAINT_CLASS */
 
       _fd_var_add_constraint(x, c);
       _fd_var_add_constraint(y, c);
diff --git a/src/constraints/minus-ne.c b/src/constraints/minus-ne.c
index 3164877..8f799e4 100644
--- a/src/constraints/minus-ne.c
+++ b/src/constraints/minus-ne.c
@@ -99,18 +99,14 @@ int fd_minus_ne_propagate(fd_constraint this, fd_int revise)
 
 fd_constraint fd_minus_ne(fd_int x, fd_int y, int k)
 {
-  fd_constraint c = _fd_constraint_new(2, 1);
+  fd_constraint c = fd__constraint_new(2, 1);
 
   if (c)
     {
       c->variables[0] = FD_INT2C_VAR(x); c->variables[1] = FD_INT2C_VAR(y);
       c->constants[0] = k;
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_MINUS_NE;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_minus_ne_propagate2;
-      c->propagator = fd_minus_ne_propagate;
-#endif /* CONSTRAINT_CLASS */
 
       _fd_var_add_constraint(x, c);
       _fd_var_add_constraint(y, c);
diff --git a/src/constraints/ne.c b/src/constraints/ne.c
index ebbc0bd..9cb9406 100644
--- a/src/constraints/ne.c
+++ b/src/constraints/ne.c
@@ -86,17 +86,13 @@ int fd_ne_propagate(fd_constraint this, fd_int revise)
 
 fd_constraint fd_ne(fd_int x, fd_int y)
 {
-  fd_constraint c = _fd_constraint_new(2, 0);
+  fd_constraint c = fd__constraint_new(2, 0);
 
   if (c)
     {
       c->variables[0] = FD_INT2C_VAR(x); c->variables[1] = FD_INT2C_VAR(y);
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_NE;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_ne_propagate2;
-      c->propagator = fd_ne_propagate;
-#endif /* CONSTRAINT_CLASS */
 
       _fd_var_add_constraint(x, c);
       _fd_var_add_constraint(y, c);
diff --git a/src/constraints/nogoods.c b/src/constraints/nogoods.c
index c77929d..503d58d 100644
--- a/src/constraints/nogoods.c
+++ b/src/constraints/nogoods.c
@@ -35,16 +35,13 @@ int fd_nogoods_propagate(fd_constraint this, fd_int revise)
 
 fd_constraint fd_nogoods(fd_int x, fd_int y, int *nogoods, int nnogoods)
 {
-  fd_constraint c = _fd_constraint_new(2, 2 * nnogoods);
+  fd_constraint c = fd__constraint_new(2, 2 * nnogoods);
 
   if (c)
     {
       c->variables[0] = FD_INT2C_VAR(x); c->variables[1] = FD_INT2C_VAR(y);
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_NOGOODS;
-#else /* CONSTRAINT_CLASS */
-      c->propagator = fd_nogoods_propagate;
-#endif /* CONSTRAINT_CLASS */
 
       memcpy(c->constants, nogoods, 2 * nnogoods * sizeof(int));
 
diff --git a/src/constraints/plus-gt.c b/src/constraints/plus-gt.c
index 990c1a3..da49e96 100644
--- a/src/constraints/plus-gt.c
+++ b/src/constraints/plus-gt.c
@@ -23,17 +23,14 @@ int fd_plus_gt_propagate(fd_constraint this, fd_int revise)
 
 fd_constraint fd_plus_gt(fd_int x, fd_int y, int k)
 {
-  fd_constraint c = _fd_constraint_new(2, 1);
+  fd_constraint c = fd__constraint_new(2, 1);
 
   if (c)
     {
       c->variables[0] = FD_INT2C_VAR(x); c->variables[1] = FD_INT2C_VAR(y);
       c->constants[0] = k;
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_PLUS_GT;
-#else /* CONSTRAINT_CLASS */
-      c->propagator = fd_plus_gt_propagate;
-#endif /* CONSTRAINT_CLASS */
 
       _fd_var_add_constraint(x, c);
       _fd_var_add_constraint(y, c);
diff --git a/src/constraints/poly-eq-k.c b/src/constraints/poly-eq-k.c
index 81e22af..c140093 100644
--- a/src/constraints/poly-eq-k.c
+++ b/src/constraints/poly-eq-k.c
@@ -1,5 +1,8 @@
 /* poly-eq-k(C, X, k) == sum(C . X) = k */
 
+// caveat: may not work correctly when there are more than one
+// occurrences of some variable
+
 static int fd_poly_eq_k_filter(fd_constraint this)
 {
   int k;
@@ -52,142 +55,99 @@ static int fd_poly_eq_k_filter(fd_constraint this)
     return FD_NOSOLUTION;
 
   if (min == max)
-    return FD_OK;
-
-  // XXX: poor man's propagation
-  if (min == k)
     {
-      for (i = 0; i < terms; ++i)
-	{
-	  int c = this->constants[i];
-
-	  if (c && mins[i] != maxs[i])
-	    {
-	      if (c > 0)
-		_fd_var_del_gt(mins[i], VAR(this, i));
-	      else
-		_fd_var_del_lt(maxs[i], VAR(this, i));
-
-	      // check needed for when variables occur more than once
-	      // and with opposite signs
-	      if (fd_domain_empty(VAR(this, i)))
-		return FD_NOSOLUTION;
-
-	      _fd_revise_connected(this, VAR(this, i));
-	    }
-	}
-
       fd__constraint_set_entailed(this);
     }
-  else if (max == k)
+  else
     {
-      for (i = 0; i < terms; ++i)
-	{
-	  int c = this->constants[i];
+      // bounds domain filtering
+      // XXX: number of iterations depends on the size of the domains
 
-	  if (c && mins[i] != maxs[i])
-	    {
-	      if (c > 0)
-		_fd_var_del_lt(maxs[i], VAR(this, i));
-	      else
-		_fd_var_del_gt(mins[i], VAR(this, i));
+      int omin, omax;
 
-	      if (fd_domain_empty(VAR(this, i)))
-		return FD_NOSOLUTION;
-
-	      _fd_revise_connected(this, VAR(this, i));
-	    }
-	}
+      do {
+	omin = min;
+	omax = max;
 
-      fd__constraint_set_entailed(this);
-    }
-  else
-    {
-      if (max > k)
 	for (i = 0; i < terms; ++i)
 	  {
 	    int c = this->constants[i];
-	    int xmin = mins[i];
-	    int xmax = maxs[i];
+	    int imin = mins[i];
+	    int imax = maxs[i];
+
+	    if (c == 0 || imin == imax)  continue;
 
 	    if (c > 0)
 	      {
-		// enforce sum{j!=i}(c[j] * min[j]) + c[i] * max[i] <= k
+		// enforce max(sum{j!=i}(c[j] * x[j])) + c[i] * min[i] >= k
 
-		if ((xmax - xmin) * c > k - min)
+		if (max + (imin - imax) * c < k)
 		  {
-		    // xmax = floor((k - min) / c) + xmin
-		    xmax = (k - min) / c + xmin;
+		    // imin = ceil((k - max) / c) + imax
+		    imin = (k - max) / c + imax;
 
-		    _fd_var_del_gt(xmax, VAR(this, i));
+		    fd_update_domain_and_check(del_lt, imin, VAR(this, i));
 
-		    if (fd_domain_empty(VAR(this, i)))
+		    min += (imin - mins[i]) * c;
+
+		    if (min > k)
 		      return FD_NOSOLUTION;
 
-		    _fd_revise_connected(this, VAR(this, i));
+		    mins[i] = imin;
 		  }
-	      }
-	    else if (c < 0)
-	      {
-		// enforce sum{j!=i}(c[j] * min[j]) + c[i] * min[i] <= k
 
-		if ((xmin - xmax) * c > k - min)
+		// enforce min(sum{j!=i}(c[j] * x[j])) + c[i] * max[i] <= k
+
+		if (min + (imax - imin) * c > k)
 		  {
-		    // xmin = ceil((k - min) / c) + xmax
-		    xmin = (k - min) / c + xmax;
+		    // imax = floor((k - min) / c) + imin
+		    imax = (k - min) / c + imin;
 
-		    _fd_var_del_lt(xmin, VAR(this, i));
+		    fd_update_domain_and_check(del_gt, imax, VAR(this, i));
 
-		    if (fd_domain_empty(VAR(this, i)))
-		      return FD_NOSOLUTION;
+		    max -= (maxs[i] - imax) * c;	// max >= k!
 
-		    _fd_revise_connected(this, VAR(this, i));
+		    maxs[i] = imax;
 		  }
 	      }
-	  }
-
-      if (min < k)
-	for (i = 0; i < terms; ++i)
-	  {
-	    int c = this->constants[i];
-	    int xmin = mins[i];
-	    int xmax = maxs[i];
-
-	    if (c > 0)
+	    else
 	      {
-		// enforce sum{j!=i}(c[j] * max[j]) + c[i] * min[i] >= k
+		// enforce max(sum{j!=i}(c[j] * x[j])) + c[i] * max[i] >= k
 
-		if ((xmax - xmin) * c > max - k)
+		if (max + (imax - imin) * c < k)
 		  {
-		    // xmin = ceil((k - max) / c) + xmax
-		    xmin = (k - max) / c + xmax;
+		    // imax = floor((k - max) / c) + imin
+		    imax = (k - max) / c + imin;
+
+		    fd_update_domain_and_check(del_gt, imax, VAR(this, i));
 
-		    _fd_var_del_lt(xmin, VAR(this, i));
+		    min += (imax - maxs[i]) * c;
 
-		    if (fd_domain_empty(VAR(this, i))) // domains may have holes
+		    if (min > k)
 		      return FD_NOSOLUTION;
 
-		    _fd_revise_connected(this, VAR(this, i));
+		    maxs[i] = imax;
 		  }
-	      }
-	    else if (c < 0)
-	      {
-		// enforce sum{j!=i}(c[j] * max[j]) + c[i] * max[i] >= k
 
-		if ((xmax - xmin) * c < k - max)
+		// enforce min(sum{j!=i}(c[j] * x[j])) + c[i] * min[i] <= k
+
+		if (min + (imin - imax) * c > k)
 		  {
-		    // xmax = floor((k - max) / c) + xmin
-		    xmax = (k - max) / c + xmin;
+		    // imin = ceil((k - min) / c) + imax
+		    imin = (k - min) / c + imax;
 
-		    _fd_var_del_gt(xmax, VAR(this, i));
+		    fd_update_domain_and_check(del_lt, imin, VAR(this, i));
 
-		    if (fd_domain_empty(VAR(this, i))) // domains may have holes
-		      return FD_NOSOLUTION;
+		    max -= (mins[i] - imin) * c;	// max >= k!
 
-		    _fd_revise_connected(this, VAR(this, i));
+		    mins[i] = imin;
 		  }
 	      }
 	  }
+      } while (min != max && (min != omin || max != omax));
+
+      if (min == max)
+	fd__constraint_set_entailed(this);
     }
 
 #ifdef CONSTRAINT_TEMPS
@@ -267,170 +227,104 @@ static int fd_poly_eq_k_propagate2(fd_constraint this, fd_int culprit)
     }
   while (x < terms);
 
-  // XXX: poor man's propagation
-  if (nmin == k)
-    {
-      for (i = 0; i < terms; ++i)
-	{
-	  int c = this->constants[i];
-
-	  if (c && mins[i] != maxs[i])
-	    {
-	      if (c > 0)
-		{
-		  _fd_var_del_gt(mins[i], VAR(this, i));
-
-		  maxs[i] = mins[i];
-		}
-	      else
-		{
-		  _fd_var_del_lt(maxs[i], VAR(this, i));
-
-		  mins[i] = maxs[i];
-		}
-
-	      if (fd_domain_empty(VAR(this, i)))
-		return FD_NOSOLUTION;
-
-	      _fd_revise_connected(this, VAR(this, i));
-	    }
-	}
+  if (nmin == min && nmax == max)
+    return FD_OK;
 
+  if (nmin == nmax)
+    {
       fd__constraint_set_entailed(this);
     }
-  else if (nmax == k)
+  else
     {
-      for (i = 0; i < terms; ++i)
-	{
-	  int c = this->constants[i];
-
-	  if (c && mins[i] != maxs[i])
-	    {
-	      if (c > 0)
-		{
-		  _fd_var_del_lt(maxs[i], VAR(this, i));
+      // bounds domain propagation
+      // XXX: number of iterations depends on the size of the domains
 
-		  mins[i] = maxs[i];
-		}
-	      else
-		{
-		  _fd_var_del_gt(mins[i], VAR(this, i));
+      int onmin, onmax;
 
-		  maxs[i] = mins[i];
-		}
+      do {
+	onmin = nmin;
+	onmax = nmax;
 
-	      if (fd_domain_empty(VAR(this, i)))
-		return FD_NOSOLUTION;
-
-	      _fd_revise_connected(this, VAR(this, i));
-	    }
-	}
-
-      fd__constraint_set_entailed(this);
-    }
-
-#if 0	// this is too expensive
-    {
-      if (nmax < max && nmax > k)
 	for (i = 0; i < terms; ++i)
 	  {
 	    int c = this->constants[i];
-	    int xmin = mins[i];
-	    int xmax = maxs[i];
+	    int imin = mins[i];
+	    int imax = maxs[i];
+
+	    if (c == 0 || imin == imax)  continue;
 
 	    if (c > 0)
 	      {
-		// enforce sum{j!=i}(c[j] * min[j]) + c[i] * max[i] <= k
+		// enforce max(sum{j!=i}(c[j] * x[j])) + c[i] * min[i] >= k
 
-		if ((xmax - xmin) * c > k - nmin)
+		if (nmax + (imin - imax) * c < k)
 		  {
-		    // xmax = floor((k - nmin) / c) + xmin
-		    xmax = (k - nmin) / c + xmin;
+		    // imin = ceil((k - nmax) / c) + imax
+		    imin = (k - nmax) / c + imax;
+
+		    fd_update_domain_and_check(del_lt, imin, VAR(this, i));
 
-		    if (_fd_var_del_gt(xmax, VAR(this, i)))
-		      {
-			if (fd_domain_empty(VAR(this, i)))
-			  return FD_NOSOLUTION;
+		    nmin += (imin - mins[i]) * c;
 
-			_fd_revise_connected(this, VAR(this, i));
-		      }
+		    if (nmin > k)
+		      return FD_NOSOLUTION;
 
-		    maxs[i] = xmax;
+		    mins[i] = imin;
 		  }
-	      }
-	    else if (c < 0)
-	      {
-		// enforce sum{j!=i}(c[j] * min[j]) + c[i] * min[i] <= k
 
-		if ((xmin - xmax) * c > k - nmin)
+		// enforce min(sum{j!=i}(c[j] * x[j])) + c[i] * max[i] <= k
+
+		if (nmin + (imax - imin) * c > k)
 		  {
-		    // xmin = ceil((k - nmin) / c) + xmax
-		    xmin = (k - nmin) / c + xmax;
+		    // imax = floor((k - nmin) / c) + imin
+		    imax = (k - nmin) / c + imin;
 
-		    if (_fd_var_del_lt(xmin, VAR(this, i)))
-		      {
-			if (fd_domain_empty(VAR(this, i)))
-			  return FD_NOSOLUTION;
+		    fd_update_domain_and_check(del_gt, imax, VAR(this, i));
 
-			_fd_revise_connected(this, VAR(this, i));
-		      }
+		    nmax -= (maxs[i] - imax) * c;	// nmax >= k!
 
-		    mins[i] = xmin;
+		    maxs[i] = imax;
 		  }
 	      }
-	  }
-
-      if (nmin > min && nmin < k)
-	for (i = 0; i < terms; ++i)
-	  {
-	    int c = this->constants[i];
-	    int xmin = mins[i];
-	    int xmax = maxs[i];
-
-	    if (c > 0)
+	    else
 	      {
-		// enforce sum{j!=i}(c[j] * max[j]) + c[i] * min[i] >= k
+		// enforce max(sum{j!=i}(c[j] * x[j])) + c[i] * max[i] >= k
 
-		if ((xmax - xmin) * c > nmax - k)
+		if (nmax + (imax - imin) * c < k)
 		  {
-		    // xmin = ceil((k - nmax) / c) + xmax
-		    xmin = (k - nmax) / c + xmax;
+		    // imax = floor((k - nmax) / c) + imin
+		    imax = (k - nmax) / c + imin;
 
-		    if (_fd_var_del_lt(xmin, VAR(this, i)))
-		      {
+		    fd_update_domain_and_check(del_gt, imax, VAR(this, i));
 
-			if (fd_domain_empty(VAR(this, i))) // domains may have holes
-			  return FD_NOSOLUTION;
+		    nmin += (imax - maxs[i]) * c;
 
-			_fd_revise_connected(this, VAR(this, i));
-		      }
+		    if (nmin > k)
+		      return FD_NOSOLUTION;
 
-		    mins[i] = xmin;
+		    maxs[i] = imax;
 		  }
-	      }
-	    else if (c < 0)
-	      {
-		// enforce sum{j!=i}(c[j] * max[j]) + c[i] * max[i] >= k
 
-		if ((xmax - xmin) * c < k - nmax)
+		// enforce min(sum{j!=i}(c[j] * x[j])) + c[i] * min[i] <= k
+
+		if (nmin + (imin - imax) * c > k)
 		  {
-		    // xmax = floor((k - nmax) / c) + xmin
-		    xmax = (k - nmax) / c + xmin;
+		    // imin = ceil((k - nmin) / c) + imax
+		    imin = (k - nmin) / c + imax;
 
-		    if (_fd_var_del_gt(xmax, VAR(this, i)))
-		      {
-			if (fd_domain_empty(VAR(this, i))) // domains may have holes
-			  return FD_NOSOLUTION;
+		    fd_update_domain_and_check(del_lt, imin, VAR(this, i));
 
-			_fd_revise_connected(this, VAR(this, i));
-		      }
+		    nmax -= (mins[i] - imin) * c;	// nmax >= k!
 
-		    maxs[i] = xmax;
+		    mins[i] = imin;
 		  }
 	      }
 	  }
+      } while (nmin != nmax && (nmin != onmin || nmax != onmax));
+
+      if (nmin == nmax)
+	fd__constraint_set_entailed(this);
     }
-#endif
 
   *base = nmin;
   *(base + 1) = nmax;
@@ -443,7 +337,7 @@ static int fd_poly_eq_k_propagate2(fd_constraint this, fd_int culprit)
 
 fd_constraint fd_poly_eq_k(int cs[], fd_int xs[], int nterms, int k)
 {
-  fd_constraint c = _fd_constraint_new(nterms, nterms + 1);
+  fd_constraint c = fd__constraint_new(nterms, nterms + 1);
   int i;
 
   if (c)
@@ -453,11 +347,8 @@ fd_constraint fd_poly_eq_k(int cs[], fd_int xs[], int nterms, int k)
       for (i = 0; i < nterms; ++i)
 	c->constants[i] = cs[i];
       c->constants[nterms] = k;
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_POLY_EQ_K;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_poly_eq_k_propagate2;
-#endif /* CONSTRAINT_CLASS */
 
       for (i = 0; i < c->nvariables; ++i)
 	_fd_var_add_constraint(VAR(c, i), c);
diff --git a/src/constraints/poly-eq.c b/src/constraints/poly-eq.c
index 0bbb135..b9bf60f 100644
--- a/src/constraints/poly-eq.c
+++ b/src/constraints/poly-eq.c
@@ -1,4 +1,7 @@
-/* poly-eq(C, X, y) == min(y) <= sum(C . X) <= max(y) */
+/* poly-eq(C, X, y) == sum(C . X) = y */
+
+// caveat: may not work correctly when there are more than one
+// occurrences of some variable
 
 static int fd_poly_eq_filter(fd_constraint this)
 {
@@ -52,154 +55,155 @@ static int fd_poly_eq_filter(fd_constraint this)
   if (min > ub || max < lb)
     return FD_NOSOLUTION;
 
-  if ((min > lb && _fd_var_del_lt(min, VAR(this, this->nvariables - 1))) |
-      (max < ub && _fd_var_del_gt(max, VAR(this, this->nvariables - 1))))
+  if (min > lb)
     {
-      if (fd_domain_empty(VAR(this, this->nvariables - 1)))
-	return FD_NOSOLUTION;
+      fd_update_domain_and_check(del_lt, min, VAR(this, this->nvariables - 1));
+
+      lb = min;
+    }
+
+  if (max < ub)
+    {
+      fd_update_domain_and_check(del_gt, max, VAR(this, this->nvariables - 1));
 
-      _fd_revise_connected(this, VAR(this, this->nvariables - 1));
+      ub = max;
     }
 
   if (min == max)
-    return FD_OK;
+    {
+      fd__constraint_set_entailed(this);
+    }
+  else
+    {
+      // bounds domain filtering
+      // XXX: number of iterations depends on the size of the domains
 
-  // XXX: poor man's propagation
-  if (min == ub)
-    for (i = 0; i < terms; ++i)
-      {
-	int c = this->constants[i];
+      int omin, omax;
 
-	if (c && mins[i] != maxs[i])
+      do {
+	omin = min;
+	omax = max;
+
+	if (max > ub)
 	  {
-	    if (c > 0)
+	    for (i = 0; i < terms; ++i)
 	      {
-		_fd_var_del_gt(mins[i], VAR(this, i));
+		int c = this->constants[i];
+		int imin = mins[i];
+		int imax = maxs[i];
 
-		// check needed for when variables occur twice and
-		// with opposite signs
-		if (fd_domain_empty(VAR(this, i)))
-		  return FD_NOSOLUTION;
+		if (c == 0 || imin == imax)  continue;
 
-		_fd_revise_connected(this, VAR(this, i));
-	      }
-	    else
-	      {
-		_fd_var_del_lt(maxs[i], VAR(this, i));
+		if (c > 0)
+		  {
+		    // enforce min(sum{j!=i}(c[j] * x[j])) + c[i] * max[i] <= max[y]
 
-		if (fd_domain_empty(VAR(this, i)))
-		  return FD_NOSOLUTION;
+		    if (min + (imax - imin) * c > ub)
+		      {
+			// imax = floor((ub - min) / c) + imin
+			imax = (ub - min) / c + imin;
 
-		_fd_revise_connected(this, VAR(this, i));
-	      }
-	  }
-      }
-  else if (max == lb)
-    for (i = 0; i < terms; ++i)
-      {
-	int c = this->constants[i];
+			fd_update_domain_and_check(del_gt, imax, VAR(this, i));
 
-	if (c && mins[i] != maxs[i])
-	  {
-	    if (c > 0)
-	      {
-		_fd_var_del_lt(maxs[i], VAR(this, i));
+			max -= (maxs[i] - imax) * c;
 
-		if (fd_domain_empty(VAR(this, i)))
-		  return FD_NOSOLUTION;
+			maxs[i] = imax;
+		      }
+		  }
+		else
+		  {
+		    // enforce min(sum{j!=i}(c[j] * x[j])) + c[i] * min[i] <= max[y]
 
-		_fd_revise_connected(this, VAR(this, i));
-	      }
-	    else
-	      {
-		_fd_var_del_gt(mins[i], VAR(this, i));
+		    if (min + (imin - imax) * c > ub)
+		      {
+			// imin = ceil((ub - min) / c) + imax
+			imin = (ub - min) / c + imax;
+
+			fd_update_domain_and_check(del_lt, imin, VAR(this, i));
 
-		if (fd_domain_empty(VAR(this, i)))
-		  return FD_NOSOLUTION;
+			max -= (mins[i] - imin) * c;
 
-		_fd_revise_connected(this, VAR(this, i));
+			mins[i] = imin;
+		      }
+		  }
 	      }
+
+	    if (max < lb)
+	      return FD_NOSOLUTION;
 	  }
-      }
-  else
-    {
-      if (max > ub)
-	for (i = 0; i < terms; ++i)
-	  {
-	    int c = this->constants[i];
-	    int xmin = mins[i];
-	    int xmax = maxs[i];
 
-	    if (c > 0)
+	if (min < lb)
+	  {
+	    for (i = 0; i < terms; ++i)
 	      {
-		if ((xmax - xmin) * c > ub - min)
+		int c = this->constants[i];
+		int imin = mins[i];
+		int imax = maxs[i];
+
+		if (c == 0 || imin == imax)  continue;
+
+		if (c > 0)
 		  {
-		    // xmax = floor((ub - min) / c) + xmin
-		    xmax = (ub - min) / c + xmin;
+		    // enforce max(sum{j!=i}(c[j] * x[j])) + c[i] * min[i] >= min[y]
+
+		    if (max + (imin - imax) * c < lb)
+		      {
+			// imin = ceil((lb - max) / c) + imax
+			imin = (lb - max) / c + imax;
 
-		    _fd_var_del_gt(xmax, VAR(this, i));
+			fd_update_domain_and_check(del_lt, imin, VAR(this, i));
 
-		    if (fd_domain_empty(VAR(this, i)))
-		      return FD_NOSOLUTION;
+			min += (imin - mins[i]) * c;
 
-		    _fd_revise_connected(this, VAR(this, i));
+			mins[i] = imin;
+		      }
 		  }
-	      }
-	    else if (c < 0)
-	      {
-		if ((xmin - xmax) * c > ub - min)
+		else
 		  {
-		    // xmin = ceil((ub - min) / c) + xmax
-		    xmin = (ub - min) / c + xmax;
+		    // enforce max(sum{j!=i}(c[j] * x[j])) + c[i] * max[i] >= min[y]
+
+		    if (max + (imax - imin) * c < lb)
+		      {
+			// imax = floor((lb - max) / c) + imin
+			imax = (lb - max) / c + imin;
 
-		    _fd_var_del_lt(xmin, VAR(this, i));
+			fd_update_domain_and_check(del_gt, imax, VAR(this, i));
 
-		    if (fd_domain_empty(VAR(this, i)))
-		      return FD_NOSOLUTION;
+			min += (imax - maxs[i]) * c;
 
-		    _fd_revise_connected(this, VAR(this, i));
+			maxs[i] = imax;
+		      }
 		  }
 	      }
+
+	    if (min > ub)
+	      return FD_NOSOLUTION;
 	  }
 
-      if (min < lb)
-	for (i = 0; i < terms; ++i)
+	if (min > lb && _fd_var_del_lt(min, VAR(this, this->nvariables - 1)))
 	  {
-	    int c = this->constants[i];
-	    int xmin = mins[i];
-	    int xmax = maxs[i];
+	    if (fd_domain_empty(VAR(this, this->nvariables - 1)))
+	      return FD_NOSOLUTION;
 
-	    if (c > 0)
-	      {
-		if ((xmax - xmin) * c > max - lb)
-		  {
-		    // xmin = ceil((lb - max) / c) + xmax
-		    xmin = (lb - max) / c + xmax;
+	    _fd_revise_connected(this, VAR(this, this->nvariables - 1));
 
-		    _fd_var_del_lt(xmin, VAR(this, i));
+	    lb = min;
+	  }
 
-		    if (fd_domain_empty(VAR(this, i))) // domains may have holes
-		      return FD_NOSOLUTION;
+	if (max < ub && _fd_var_del_gt(max, VAR(this, this->nvariables - 1)))
+	  {
+	    if (fd_domain_empty(VAR(this, this->nvariables - 1)))
+	      return FD_NOSOLUTION;
 
-		    _fd_revise_connected(this, VAR(this, i));
-		  }
-	      }
-	    else if (c < 0)
-	      {
-		if ((xmax - xmin) * c < lb - max)
-		  {
-		    // xmax = floor((lb - max) / c) + xmin
-		    xmax = (lb - max) / c + xmin;
+	    _fd_revise_connected(this, VAR(this, this->nvariables - 1));
 
-		    _fd_var_del_gt(xmax, VAR(this, i));
+	    ub = max;
+	  }
 
-		    if (fd_domain_empty(VAR(this, i))) // domains may have holes
-		      return FD_NOSOLUTION;
+      } while (min != max && (min != omin || max != omax));
 
-		    _fd_revise_connected(this, VAR(this, i));
-		  }
-	      }
-	  }
+      if (min == max)
+	fd__constraint_set_entailed(this);
     }
 
 #ifdef CONSTRAINT_TEMPS
@@ -226,6 +230,7 @@ static int fd_poly_eq_propagate2(fd_constraint this, fd_int culprit)
   int *base;
   int x, c;
   int nmin, nmin_x, nmax, nmax_x;
+  int nlb, nub;
 
   if (!fd__constraint_data_valid(this))
     return fd_poly_eq_filter(this);	// ignores culprit
@@ -237,16 +242,15 @@ static int fd_poly_eq_propagate2(fd_constraint this, fd_int culprit)
   mins = base + 4;
   maxs = mins + terms;
 
-  lb = *base;
-  ub = *(base + 1);
+  nlb = lb = *base;
+  nub = ub = *(base + 1);
 
-  min = *(base + 2);
-  max = *(base + 3);
+  nmin = min = *(base + 2);
+  nmax = max = *(base + 3);
 
   if (culprit == VAR(this, this->nvariables - 1))
     {
       // the culprit is the sum variable
-      int nlb, nub;
 
       nlb = _fd_var_min(culprit);
       nub = _fd_var_max(culprit);
@@ -254,203 +258,205 @@ static int fd_poly_eq_propagate2(fd_constraint this, fd_int culprit)
       if (nlb == lb && nub == ub)
 	return FD_OK;
 
-      if (nlb != lb)
-	{
-	  if (max < nlb)
-	    return FD_NOSOLUTION;
+      if (min > nub || max < nlb)
+	return FD_NOSOLUTION;
+    }
+  else
+    {
+      // the culprit appears in one of the terms, find out which one(s)
+      for (x = 0; culprit != VAR(this, x); ++x)
+	;
 
-	  if (max == nlb)
-	    for (i = 0; i < terms; ++i)
-	      {
-		if (this->constants[i] > 0)
-		  {
-		    if (_fd_var_del_lt(maxs[i], VAR(this, i)))
-		      {
-			if (fd_domain_empty(VAR(this, i)))
-			  return FD_NOSOLUTION;
+      nmin_x = _fd_var_min(VAR(this, x));
+      nmax_x = _fd_var_max(VAR(this, x));
 
-			_fd_revise_connected(this, VAR(this, i));
-		      }
-		  }
-		else if (this->constants[i] < 0)
-		  {
-		    if (_fd_var_del_gt(mins[i], VAR(this, i)))
-		      {
-			if (fd_domain_empty(VAR(this, i)))
-			  return FD_NOSOLUTION;
+      if (nmin_x == mins[x] && nmax_x == maxs[x])
+	return FD_OK;
 
-			_fd_revise_connected(this, VAR(this, i));
-		      }
-		  }
-	      }
+      do
+	{
+	  c = this->constants[x];
+
+	  if (c > 0)
+	    {
+	      nmin = nmin + (nmin_x - mins[x]) * c;
+	      nmax = nmax - (maxs[x] - nmax_x) * c;
+	    }
+	  else if (c < 0)
+	    {
+	      nmin = nmin - (maxs[x] - nmax_x) * c;
+	      nmax = nmax + (nmin_x - mins[x]) * c;
+	    }
+
+	  if (nmin > ub || nmax < lb)
+	    return FD_NOSOLUTION;
 
-      if (min < nlb)
-	for (i = 0; i < terms; ++i)
-	  {
-	    int c = this->constants[i];
-	    int xmin = mins[i];
-	    int xmax = maxs[i];
+	  mins[x] = nmin_x;
+	  maxs[x] = nmax_x;
 
-	    if (c > 0)
-	      {
-		if ((xmax - xmin) * c > max - nlb)
-		  {
-		    // xmin = ceil((nlb - max) / c) + xmax
-		    xmin = (nlb - max) / c + xmax;
+	  while (++x < terms && culprit != VAR(this, x))
+	    ;
+	}
+      while (x < terms);
 
-		    _fd_var_del_lt(xmin, VAR(this, i));
+      if (nmin == min && nmax == max)
+	return FD_OK;
 
-		    if (fd_domain_empty(VAR(this, i))) // domains may have holes
-		      return FD_NOSOLUTION;
+      if (nmin > lb)
+	{
+	  fd_update_domain_and_check(del_lt, nmin, VAR(this, this->nvariables - 1));
 
-		    _fd_revise_connected(this, VAR(this, i));
-		  }
-	      }
-	    else if (c < 0)
-	      {
-		if ((xmax - xmin) * c < nlb - max)
-		  {
-		    // xmax = floor((nlb - max) / c) + xmin
-		    xmax = (nlb - max) / c + xmin;
+	  nlb = nmin;
+	}
 
-		    _fd_var_del_gt(xmax, VAR(this, i));
+      if (nmax < ub)
+	{
+	  fd_update_domain_and_check(del_gt, nmax, VAR(this, this->nvariables - 1));
 
-		    if (fd_domain_empty(VAR(this, i))) // domains may have holes
-		      return FD_NOSOLUTION;
+	  nub = nmax;
+	}
+    }
 
-		    _fd_revise_connected(this, VAR(this, i));
-		  }
-	      }
-	  }
+  // (nmin, nlb, nub, nmax) != (min, lb, ub, max)
 
-	  *base = nlb;
-	}
+  if (nmin == nmax)
+    {
+      fd__constraint_set_entailed(this);
+    }
+  else
+    {
+      // bounds domain propagation
+      // XXX: number of iterations depends on the size of the domains
 
-      if (nub != ub)
-	{
-	  if (min > nub)
-	    return FD_NOSOLUTION;
+      int onmin, onmax;
 
-	  if (min == nub)
+      do {
+	onmin = nmin;
+	onmax = nmax;
+
+	if (nmin < nlb)
+	  {
 	    for (i = 0; i < terms; ++i)
 	      {
-		if (this->constants[i] > 0)
+		int c = this->constants[i];
+		int imin = mins[i];
+		int imax = maxs[i];
+
+		if (c == 0 || imin == imax)  continue;
+
+		if (c > 0)
 		  {
-		    if (_fd_var_del_gt(mins[i], VAR(this, i)))
+		    // enforce max(sum{j!=i}(c[j] * x[j])) + c[i] * min[i] >= min[y]
+
+		    if (nmax + (imin - imax) * c < nlb)
 		      {
-			if (fd_domain_empty(VAR(this, i)))
-			  return FD_NOSOLUTION;
+			// imin = ceil((nlb - nmax) / c) + imax
+			imin = (nlb - nmax) / c + imax;
+
+			fd_update_domain_and_check(del_lt, imin, VAR(this, i));
+
+			nmin += (imin - mins[i]) * c;
 
-			_fd_revise_connected(this, VAR(this, i));
+			mins[i] = imin;
 		      }
 		  }
-		else if (this->constants[i] < 0)
+		else
 		  {
-		    if (_fd_var_del_lt(maxs[i], VAR(this, i)))
+		    // enforce max(sum{j!=i}(c[j] * x[j])) + c[i] * max[i] >= min[y]
+
+		    if (nmax + (imax - imin) * c < nlb)
 		      {
-			if (fd_domain_empty(VAR(this, i)))
-			  return FD_NOSOLUTION;
+			// imax = floor((nlb - nmax) / c) + imin
+			imax = (nlb - nmax) / c + imin;
+
+			fd_update_domain_and_check(del_gt, imax, VAR(this, i));
 
-			_fd_revise_connected(this, VAR(this, i));
+			nmin += (imax - maxs[i]) * c;
+
+			maxs[i] = imax;
 		      }
 		  }
 	      }
 
-      if (max > nub)
-	for (i = 0; i < terms; ++i)
-	  {
-	    int c = this->constants[i];
-	    int xmin = mins[i];
-	    int xmax = maxs[i];
+	    if (nmin > nub)
+	      return FD_NOSOLUTION;
+	  }
 
-	    if (c > 0)
+	if (nmax > nub)
+	  {
+	    for (i = 0; i < terms; ++i)
 	      {
-		if ((xmax - xmin) * c > nub - min)
+		int c = this->constants[i];
+		int imin = mins[i];
+		int imax = maxs[i];
+
+		if (c == 0 || imin == imax)  continue;
+
+		if (c > 0)
 		  {
-		    // xmax = floor((nub - min) / c) + xmin
-		    xmax = (nub - min) / c + xmin;
+		    // enforce min(sum{j!=i}(c[j] * x[j])) + c[i] * max[i] <= max[y]
 
-		    if (_fd_var_del_gt(xmax, VAR(this, i)))
+		    if (nmin + (imax - imin) * c > nub)
 		      {
-			if (fd_domain_empty(VAR(this, i)))
-			  return FD_NOSOLUTION;
+			// imax = floor((nub - nmin) / c) + imin
+			imax = (nub - nmin) / c + imin;
 
-			_fd_revise_connected(this, VAR(this, i));
+			fd_update_domain_and_check(del_gt, imax, VAR(this, i));
+
+			nmax -= (maxs[i] - imax) * c;
+
+			maxs[i] = imax;
 		      }
 		  }
-	      }
-	    else if (c < 0)
-	      {
-		if ((xmin - xmax) * c > nub - min)
+		else
 		  {
-		    // xmin = ceil((nub - min) / c) + xmax
-		    xmin = (nub - min) / c + xmax;
+		    // enforce min(sum{j!=i}(c[j] * x[j])) + c[i] * min[i] <= max[y]
 
-		    if (_fd_var_del_lt(xmin, VAR(this, i)))
+		    if (nmin + (imin - imax) * c > nub)
 		      {
-			if (fd_domain_empty(VAR(this, i)))
-			  return FD_NOSOLUTION;
+			// imin = ceil((nub - nmin) / c) + imax
+			imin = (nub - nmin) / c + imax;
+
+			fd_update_domain_and_check(del_lt, imin, VAR(this, i));
 
-			_fd_revise_connected(this, VAR(this, i));
+			nmax -= (mins[i] - imin) * c;
+
+			mins[i] = imin;
 		      }
 		  }
 	      }
-	  }
-
-	  *(base + 1) = nub;
-	}
-
-      return FD_OK;
-    }
-
-  // the culprit appears in one of the terms, find out which one(s)
-  for (x = 0; culprit != VAR(this, x); ++x)
-    ;
-
-  nmin_x = _fd_var_min(VAR(this, x));
-  nmax_x = _fd_var_max(VAR(this, x));
-
-  if (nmin_x == mins[x] && nmax_x == maxs[x])
-    return FD_OK;
 
-  nmin = min;
-  nmax = max;
+	    if (nmax < nlb)
+	      return FD_NOSOLUTION;
+	  }
 
-  do
-    {
-      c = this->constants[x];
+	if (nmin > nlb && _fd_var_del_lt(nmin, VAR(this, this->nvariables - 1)))
+	  {
+	    if (fd_domain_empty(VAR(this, this->nvariables - 1)))
+	      return FD_NOSOLUTION;
 
-      if (c > 0)
-	{
-	  nmin = nmin + (nmin_x - mins[x]) * c;
-	  nmax = nmax - (maxs[x] - nmax_x) * c;
-	}
-      else if (c < 0)
-	{
-	  nmin = nmin - (maxs[x] - nmax_x) * c;
-	  nmax = nmax + (nmin_x - mins[x]) * c;
-	}
+	    _fd_revise_connected(this, VAR(this, this->nvariables - 1));
 
-      if (nmin > ub || nmax < lb)
-	return FD_NOSOLUTION;
+	    nlb = nmin;
+	  }
 
-      mins[x] = nmin_x;
-      maxs[x] = nmax_x;
+	if (nmax < nub && _fd_var_del_gt(nmax, VAR(this, this->nvariables - 1)))
+	  {
+	    if (fd_domain_empty(VAR(this, this->nvariables - 1)))
+	      return FD_NOSOLUTION;
 
-      while (++x < terms && culprit != VAR(this, x))
-	;
-    }
-  while (x < terms);
+	    _fd_revise_connected(this, VAR(this, this->nvariables - 1));
 
-  if ((nmin > lb && _fd_var_del_lt(nmin, VAR(this, this->nvariables - 1))) |
-      (nmax < ub && _fd_var_del_gt(nmax, VAR(this, this->nvariables - 1))))
-    {
-      if (fd_domain_empty(VAR(this, this->nvariables - 1)))
-	return FD_NOSOLUTION;
+	    nub = nmax;
+	  }
+      } while (nmin != nmax && (nmin != onmin || nmax != onmax));
 
-      _fd_revise_connected(this, VAR(this, this->nvariables - 1));
+      if (nmin == nmax)
+	fd__constraint_set_entailed(this);
     }
 
+  *(base + 0) = nlb;
+  *(base + 1) = nub;
   *(base + 2) = nmin;
   *(base + 3) = nmax;
 
@@ -462,8 +468,15 @@ static int fd_poly_eq_propagate2(fd_constraint this, fd_int culprit)
 
 fd_constraint fd_poly_eq(int cs[], fd_int xs[], int nterms, fd_int y)
 {
-  fd_constraint c = _fd_constraint_new(nterms + 1, nterms);
-  int i;
+  fd_constraint fd_poly_eq_k(int[], fd_int[], int, int);
+  fd_constraint c;
+  int i, k;
+
+  // specialise when y is a singleton
+  if (fd_var_single(y, &k))
+    return fd_poly_eq_k(cs, xs, nterms, k);
+
+  c = fd__constraint_new(nterms + 1, nterms);
 
   if (c)
     {
@@ -472,11 +485,8 @@ fd_constraint fd_poly_eq(int cs[], fd_int xs[], int nterms, fd_int y)
       c->variables[nterms] = FD_INT2C_VAR(y);
       for (i = 0; i < nterms; ++i)
 	c->constants[i] = cs[i];
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_POLY_EQ;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_poly_eq_propagate2;
-#endif /* CONSTRAINT_CLASS */
 
       for (i = 0; i < c->nvariables; ++i)
 	_fd_var_add_constraint(VAR(c, i), c);
diff --git a/src/constraints/poly-le-k.c b/src/constraints/poly-le-k.c
new file mode 100644
index 0000000..fb77d39
--- /dev/null
+++ b/src/constraints/poly-le-k.c
@@ -0,0 +1,346 @@
+/* poly-le-k(C, X, k) == sum(C . X) <= k */
+
+static int fd_poly_le_k_filter(fd_constraint this)
+{
+  int k;
+  int min, max;
+  int terms = this->nvariables;
+  int *mins, *maxs;
+  int i;
+#ifdef CONSTRAINT_TEMPS
+  int *base;
+
+  assert(!fd__constraint_data_valid(this));
+
+  if (!constraint_memory[this->index])
+    constraint_memory[this->index] = malloc((2 * terms + 2) * sizeof(int));
+
+  base = constraint_memory[this->index];
+
+  mins = base + 2;
+  maxs = mins + terms;
+#else
+  mins = alloca(terms * sizeof(*mins));
+  maxs = alloca(terms * sizeof(*maxs));
+#endif
+
+  k = this->constants[terms];
+
+  // sum the minima and the maxima of the terms
+  min = max = 0;
+
+  for (i = 0; i < terms; ++i)
+    {
+      int vl, vh;
+
+      if (this->constants[i] > 0)
+	{
+	  vl = mins[i] = _fd_var_min(VAR(this, i));
+	  vh = maxs[i] = _fd_var_max(VAR(this, i));
+	}
+      else
+	{
+	  vl = maxs[i] = _fd_var_max(VAR(this, i));
+	  vh = mins[i] = _fd_var_min(VAR(this, i));
+	}
+
+      min += this->constants[i] * vl;
+      max += this->constants[i] * vh;
+    }
+
+  if (min > k)
+    return FD_NOSOLUTION;
+
+  if (max <= k)
+    {
+      fd__constraint_set_entailed(this);
+    }
+  else if (min == k)
+    {
+      for (i = 0; i < terms; ++i)
+	{
+	  int c = this->constants[i];
+
+	  if (c == 0 || mins[i] == maxs[i])  continue;
+
+	  if (c > 0)
+	    {
+	      _fd_var_del_gt(mins[i], VAR(this, i));
+
+	      // check needed for when variables occur more than once
+	      // and with opposite signs
+	      if (fd_domain_empty(VAR(this, i)))
+		return FD_NOSOLUTION;
+
+	      _fd_revise_connected(this, VAR(this, i));
+
+	      maxs[i] = mins[i];
+	    }
+	  else
+	    {
+	      _fd_var_del_lt(maxs[i], VAR(this, i));
+
+	      // check needed for when variables occur more than once
+	      // and with opposite signs
+	      if (fd_domain_empty(VAR(this, i)))
+		return FD_NOSOLUTION;
+
+	      _fd_revise_connected(this, VAR(this, i));
+
+	      mins[i] = maxs[i];
+	    }
+	}
+
+      max = min;
+
+      fd__constraint_set_entailed(this);
+    }
+  else // max > k
+    {
+      // bounds domain filtering
+
+      for (i = 0; i < terms; ++i)
+	{
+	  int c = this->constants[i];
+	  int imin = mins[i];
+	  int imax = maxs[i];
+
+	  if (c > 0)
+	    {
+	      // enforce min(sum{j!=i}(c[j] * x[j])) + c[i] * max[i] <= k
+
+	      if (min + (imax - imin) * c > k)
+		{
+		  // max[i] = floor((k - min) / c[i]) + min[i]
+		  imax = (k - min) / c + imin;
+
+		  _fd_var_del_gt(imax, VAR(this, i));
+
+		  if (fd_domain_empty(VAR(this, i)))
+		    return FD_NOSOLUTION;
+
+		  _fd_revise_connected(this, VAR(this, i));
+
+		  max -= (maxs[i] - imax) * c;
+
+		  maxs[i] = imax;
+		}
+	    }
+	  else // c < 0 (does nothing if c is 0)
+	    {
+	      // enforce min(sum{j!=i}(c[j] * x[j])) + c[i] * min[i] <= k
+
+	      if (min + (imin - imax) * c > k)
+		{
+		  // min[i] = ceil((k - min) / c[i]) + max[i]
+		  imin = (k - min) / c + imax;
+
+		  _fd_var_del_lt(imin, VAR(this, i));
+
+		  if (fd_domain_empty(VAR(this, i)))
+		    return FD_NOSOLUTION;
+
+		  _fd_revise_connected(this, VAR(this, i));
+
+		  max -= (mins[i] - imin) * c;
+
+		  mins[i] = imin;
+		}
+	    }
+	}
+
+      if (max <= k)
+	fd__constraint_set_entailed(this);
+    }
+
+#ifdef CONSTRAINT_TEMPS
+  // save values
+  *base = min;
+  *(base + 1) = max;
+
+  fd__constraint_remember(this);
+#endif
+
+  return FD_OK;
+}
+
+static int fd_poly_le_k_propagate2(fd_constraint this, fd_int culprit)
+{
+#ifdef CONSTRAINT_TEMPS
+  int k;
+  int min, max;
+  int terms = this->nvariables;
+  int *mins, *maxs;
+  int i;
+  int *base;
+  int x, c;
+  int nmin, nmin_x, nmax, nmax_x;
+
+  if (!fd__constraint_data_valid(this))
+    return fd_poly_le_k_filter(this);	// ignores culprit
+
+  // bounds filtering
+
+  base = constraint_memory[this->index];
+
+  mins = base + 2;
+  maxs = mins + terms;
+
+  min = *base;
+  max = *(base + 1);
+
+  k = this->constants[terms];
+
+  // find the (first) term where the culprit appears
+  for (x = 0; culprit != VAR(this, x); ++x)
+    ;
+
+  nmin_x = _fd_var_min(VAR(this, x));
+  nmax_x = _fd_var_max(VAR(this, x));
+
+  if (nmin_x == mins[x] && nmax_x == maxs[x])
+    return FD_OK;
+
+  nmin = min;
+  nmax = max;
+
+  do
+    {
+      c = this->constants[x];
+
+      if (c > 0)
+	{
+	  nmin = nmin + (nmin_x - mins[x]) * c;
+	  nmax = nmax - (maxs[x] - nmax_x) * c;
+	}
+      else if (c < 0)
+	{
+	  nmin = nmin - (maxs[x] - nmax_x) * c;
+	  nmax = nmax + (nmin_x - mins[x]) * c;
+	}
+
+      if (nmin > k)
+	return FD_NOSOLUTION;
+
+      mins[x] = nmin_x;
+      maxs[x] = nmax_x;
+
+      // search for the next term where the culprit appears
+      while (++x < terms && culprit != VAR(this, x))
+	;
+    }
+  while (x < terms);
+
+  if (nmin == min && nmax == max)
+    return FD_OK;
+
+  if (nmax <= k)
+    {
+      fd__constraint_set_entailed(this);
+    }
+  else if (nmin == k)
+    {
+      for (i = 0; i < terms; ++i)
+	{
+	  int c = this->constants[i];
+
+	  if (c == 0 || mins[i] == maxs[i])  continue;
+
+	  if (c > 0)
+	    {
+	      fd_update_domain_and_check(del_gt, mins[i], VAR(this, i));
+
+	      maxs[i] = mins[i];
+	    }
+	  else
+	    {
+	      fd_update_domain_and_check(del_lt, maxs[i], VAR(this, i));
+
+	      mins[i] = maxs[i];
+	    }
+	}
+
+      nmax = nmin;
+
+      fd__constraint_set_entailed(this);
+    }
+  else if (nmin > min)	// nmax > k
+    {
+      // bounds domain propagation
+
+      for (i = 0; i < terms; ++i)
+	{
+	  int c = this->constants[i];
+	  int imin = mins[i];
+	  int imax = maxs[i];
+
+	  if (c > 0)
+	    {
+	      // enforce min(sum{j!=i}(c[j] * x[j])) + c[i] * max[i] <= k
+
+	      if (nmin + (imax - imin) * c > k)
+		{
+		  // max[i] = floor((k - nmin) / c[i]) + min[i]
+		  imax = (k - nmin) / c + imin;
+
+		  fd_update_domain_and_check(del_gt, imax, VAR(this, i));
+
+		  nmax -= (maxs[i] - imax) * c;
+
+		  maxs[i] = imax;
+		}
+	    }
+	  else // c < 0 (does nothing if c is 0)
+	    {
+	      // enforce min(sum{j!=i}(c[j] * x[j])) + c[i] * min[i] <= k
+
+	      if (nmin + (imin - imax) * c > k)
+		{
+		  // min[i] = ceil((k - nmin) / c[i]) + max[i]
+		  imin = (k - nmin) / c + imax;
+
+		  fd_update_domain_and_check(del_lt, imin, VAR(this, i));
+
+		  nmax -= (mins[i] - imin) * c;
+
+		  mins[i] = imin;
+		}
+	    }
+	}
+
+      if (nmax <= k)
+	fd__constraint_set_entailed(this);
+    }
+
+  *base = nmin;
+  *(base + 1) = nmax;
+
+  return FD_OK;
+#else /* CONSTRAINT_TEMPS */
+  return fd_poly_le_k_filter(this);	// ignores culprit
+#endif /* CONSTRAINT_TEMPS */
+}
+
+fd_constraint fd_poly_le_k(int cs[], fd_int xs[], int nterms, int k)
+{
+  fd_constraint c = fd__constraint_new(nterms, nterms + 1);
+  int i;
+
+  if (c)
+    {
+      for (i = 0; i < nterms; ++i)
+	c->variables[i] = FD_INT2C_VAR(xs[i]);
+      for (i = 0; i < nterms; ++i)
+	c->constants[i] = cs[i];
+      c->constants[nterms] = k;
+
+      c->kind = FD_CONSTR_POLY_LE_K;
+
+      for (i = 0; i < c->nvariables; ++i)
+	_fd_var_add_constraint(VAR(c, i), c);
+
+      _fd_add_constraint(c);
+    }
+
+  return c;
+}
diff --git a/src/constraints/poly-ne-k.c b/src/constraints/poly-ne-k.c
index 6fefc55..5b7eebd 100644
--- a/src/constraints/poly-ne-k.c
+++ b/src/constraints/poly-ne-k.c
@@ -199,11 +199,13 @@ static int fd_poly_ne_k_propagate2(fd_constraint this, fd_int culprit)
 	if (this->constants[i] && mins[i] != maxs[i])
 	  break;
 
+      // m = sum{j != i} c[j] * x[j]
       if (this->constants[i] > 0)
-	m = min - this->constants[i] * mins[i];
+	m = nmin - this->constants[i] * mins[i];
       else
-	m = min - this->constants[i] * maxs[i];
+	m = nmin - this->constants[i] * maxs[i];
 
+      // v * c[i] + m = k (if v were a real value)
       v = (k - m) / this->constants[i];
 
       if (v * this->constants[i] + m == k)
@@ -224,7 +226,7 @@ static int fd_poly_ne_k_propagate2(fd_constraint this, fd_int culprit)
 
 fd_constraint fd_poly_ne_k(int cs[], fd_int xs[], int nterms, int k)
 {
-  fd_constraint c = _fd_constraint_new(nterms, nterms + 1);
+  fd_constraint c = fd__constraint_new(nterms, nterms + 1);
   int i;
 
   if (c)
@@ -234,11 +236,8 @@ fd_constraint fd_poly_ne_k(int cs[], fd_int xs[], int nterms, int k)
       for (i = 0; i < nterms; ++i)
 	c->constants[i] = cs[i];
       c->constants[nterms] = k;
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_POLY_NE_K;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_poly_ne_k_propagate2;
-#endif /* CONSTRAINT_CLASS */
 
       for (i = 0; i < c->nvariables; ++i)
 	_fd_var_add_constraint(VAR(c, i), c);
diff --git a/src/constraints/poly-ne.c b/src/constraints/poly-ne.c
index 70b3a39..eb2ad18 100644
--- a/src/constraints/poly-ne.c
+++ b/src/constraints/poly-ne.c
@@ -237,11 +237,13 @@ static int fd_poly_ne_propagate2(fd_constraint this, fd_int culprit)
 	if (this->constants[i] && mins[i] != maxs[i])
 	  break;
 
+      // m = sum{j != i} c[j] * x[j]
       if (this->constants[i] > 0)
-	m = min - this->constants[i] * mins[i];
+	m = nmin - this->constants[i] * mins[i];
       else
-	m = min - this->constants[i] * maxs[i];
+	m = nmin - this->constants[i] * maxs[i];
 
+      // v * c[i] + m = y (if v were a real value)
       v = (lb - m) / this->constants[i];
 
       if (v * this->constants[i] + m == lb)
@@ -262,7 +264,7 @@ static int fd_poly_ne_propagate2(fd_constraint this, fd_int culprit)
 
 fd_constraint fd_poly_ne(int cs[], fd_int xs[], int nterms, fd_int y)
 {
-  fd_constraint c = _fd_constraint_new(nterms + 1, nterms);
+  fd_constraint c = fd__constraint_new(nterms + 1, nterms);
   int i;
 
   if (c)
@@ -272,11 +274,8 @@ fd_constraint fd_poly_ne(int cs[], fd_int xs[], int nterms, fd_int y)
       c->variables[nterms] = FD_INT2C_VAR(y);
       for (i = 0; i < nterms; ++i)
 	c->constants[i] = cs[i];
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_POLY_NE;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_poly_ne_propagate2;
-#endif /* CONSTRAINT_CLASS */
 
       for (i = 0; i < c->nvariables; ++i)
 	_fd_var_add_constraint(VAR(c, i), c);
diff --git a/src/constraints/sum-prod.c b/src/constraints/sum-prod.c
index 6bad8ee..cd30c6b 100644
--- a/src/constraints/sum-prod.c
+++ b/src/constraints/sum-prod.c
@@ -393,7 +393,7 @@ static int fd_sum_prod_propagate2(fd_constraint this, fd_int culprit)
 
 fd_constraint fd_sum_prod(fd_int *xs, fd_int *ys, int nvariables, int k)
 {
-  fd_constraint c = _fd_constraint_new(2 * nvariables, 1);
+  fd_constraint c = fd__constraint_new(2 * nvariables, 1);
   int i;
 
   if (c)
@@ -403,11 +403,8 @@ fd_constraint fd_sum_prod(fd_int *xs, fd_int *ys, int nvariables, int k)
       for (; i < 2 * nvariables; ++i)
 	c->variables[i] = FD_INT2C_VAR(ys[i - nvariables]);
       c->constants[0] = k;
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_SUM_PROD;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_sum_prod_propagate2;
-#endif /* CONSTRAINT_CLASS */
 
       for (i = 0; i < 2 * nvariables; ++i)
 	_fd_var_add_constraint(VAR(c, i), c);
diff --git a/src/constraints/sum.c b/src/constraints/sum.c
index add543a..8225477 100644
--- a/src/constraints/sum.c
+++ b/src/constraints/sum.c
@@ -177,7 +177,7 @@ static int fd_sum_propagate2(fd_constraint this, fd_int culprit)
 
 fd_constraint fd_sum(fd_int *variables, int nvariables, int k)
 {
-  fd_constraint c = _fd_constraint_new(nvariables, 1);
+  fd_constraint c = fd__constraint_new(nvariables, 1);
   int i;
 
   if (c)
@@ -185,11 +185,8 @@ fd_constraint fd_sum(fd_int *variables, int nvariables, int k)
       for (i = 0; i < nvariables; ++i)
 	c->variables[i] = FD_INT2C_VAR(variables[i]);
       c->constants[0] = k;
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_SUM;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_sum_propagate2;
-#endif /* CONSTRAINT_CLASS */
 
       for (i = 0; i < nvariables; ++i)
 	_fd_var_add_constraint(VAR(c, i), c);
diff --git a/src/constraints/sum2.c b/src/constraints/sum2.c
index ddf5541..0dd7c8f 100644
--- a/src/constraints/sum2.c
+++ b/src/constraints/sum2.c
@@ -94,7 +94,7 @@ static int fd_sum2_propagate2(fd_constraint this, fd_int culprit)
 
 fd_constraint fd_sum2(fd_int *variables, int nvariables, fd_int x)
 {
-  fd_constraint c = _fd_constraint_new(nvariables + 1, 0);
+  fd_constraint c = fd__constraint_new(nvariables + 1, 0);
   int i;
 
   if (c)
@@ -102,11 +102,8 @@ fd_constraint fd_sum2(fd_int *variables, int nvariables, fd_int x)
       for (i = 0; i < nvariables; ++i)
 	c->variables[i] = FD_INT2C_VAR(variables[i]);
       c->variables[nvariables] = FD_INT2C_VAR(x);
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_SUM2;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_sum2_propagate2;
-#endif /* CONSTRAINT_CLASS */
 
       for (i = 0; i < nvariables + 1; ++i)
 	_fd_var_add_constraint(VAR(c, i), c);
diff --git a/src/constraints/var-eq-minus.c b/src/constraints/var-eq-minus.c
index 0fd59da..d403b5d 100644
--- a/src/constraints/var-eq-minus.c
+++ b/src/constraints/var-eq-minus.c
@@ -1,4 +1,4 @@
-/* X = Y - Z */
+/* x = y - z */
 
 static int fd_var_eq_minus_filter(fd_constraint this)
 {
@@ -10,7 +10,7 @@ static int fd_var_eq_minus_filter(fd_constraint this)
   y = VAR(this, 1);
   z = VAR(this, 2);
 
-  // XXX: just do bounds consistency, for now
+  // bounds consistency
 
   xmin = _fd_var_min(x);
   xmax = _fd_var_max(x);
@@ -28,6 +28,15 @@ static int fd_var_eq_minus_filter(fd_constraint this)
       _fd_revise_connected(NULL, y);
     }
 
+#ifndef DISABLE_ENTAILED
+  if ((xmin == xmax) + (zmin == zmax) == 2)
+    {
+      fd__constraint_set_entailed(this);
+
+      return FD_OK;
+    }
+#endif
+
   ymin = _fd_var_min(y);
   ymax = _fd_var_max(y);
 
@@ -54,98 +63,129 @@ static int fd_var_eq_minus_filter(fd_constraint this)
       _fd_revise_connected(NULL, x);
     }
 
-#if CONSTRAINT_TEMPS > 3
-  if (fd_var_single(x, 0) && fd_var_single(y, 0) && fd_var_single(z, 0))
+#ifndef DISABLE_ENTAILED
+  if ((ymin == ymax) + (zmin == zmax) == 2)
     fd__constraint_set_entailed(this);
 #endif
 
   return FD_OK;
 }
 
-int fd_var_eq_minus_propagate2(fd_constraint this, fd_int culprit)
+static int fd_var_eq_minus_propagate2(fd_constraint this, fd_int culprit)
 {
   fd_int x, y, z;
+  int xmin, xmax, ymin, ymax, zmin, zmax;
   int changed_x = 0, changed_y = 0, changed_z = 0;
 
   x = VAR(this, 0);
   y = VAR(this, 1);
   z = VAR(this, 2);
 
-  // XXX: just do bounds consistency, for now
+  // bounds consistency
 
   if (culprit == x)
     {
-      changed_y =
-	_fd_var_del_lt(_fd_var_min(x) + _fd_var_min(z), y);
-      changed_y |=
-	_fd_var_del_gt(_fd_var_max(x) + _fd_var_max(z), y);
+      xmin = _fd_var_min(x);
+      xmax = _fd_var_max(x);
 
-      if (changed_y && fd_domain_empty(y))
-	return FD_NOSOLUTION;
+      changed_y = _fd_var_del_lt(xmin + _fd_var_min(z), y) |
+	_fd_var_del_gt(xmax + _fd_var_max(z), y);
+
+      if (changed_y)
+	{
+	  if (fd_domain_empty(y))
+	    return FD_NOSOLUTION;
+
+	  _fd_revise_connected(NULL, y);	// XXX: NULL or this?
+	}
+
+      ymin = _fd_var_min(y);
+      ymax = _fd_var_max(y);
 
       changed_z =
-	_fd_var_del_lt(_fd_var_min(y) - _fd_var_max(x), z);
-      changed_z |=
-	_fd_var_del_gt(_fd_var_max(y) - _fd_var_min(x), z);
+	_fd_var_del_lt(ymin - xmax, z) | _fd_var_del_gt(ymax - xmin, z);
 
-      if (changed_z && fd_domain_empty(z))
-	return FD_NOSOLUTION;
+      if (changed_z)
+	{
+	  if (fd_domain_empty(z))
+	    return FD_NOSOLUTION;
+
+	  _fd_revise_connected(NULL, z);	// XXX: NULL or this?
+	}
     }
   else if (culprit == y)
     {
-      changed_x =
-	_fd_var_del_lt(_fd_var_min(y) - _fd_var_max(z), x);
-      changed_x |=
-	_fd_var_del_gt(_fd_var_max(y) - _fd_var_min(z), x);
+      ymin = _fd_var_min(y);
+      ymax = _fd_var_max(y);
 
-      if (changed_x && fd_domain_empty(x))
-	return FD_NOSOLUTION;
+      changed_x = _fd_var_del_lt(ymin - _fd_var_max(z), x) |
+	_fd_var_del_gt(ymax - _fd_var_min(z), x);
+
+      if (changed_x)
+	{
+	  if (fd_domain_empty(x))
+	    return FD_NOSOLUTION;
+
+	  _fd_revise_connected(NULL, x);	// XXX: NULL or this?
+	}
+
+      xmin = _fd_var_min(x);
+      xmax = _fd_var_max(x);
 
       changed_z =
-	_fd_var_del_lt(_fd_var_min(y) - _fd_var_max(x), z);
-      changed_z |=
-	_fd_var_del_gt(_fd_var_max(y) - _fd_var_min(x), z);
+	_fd_var_del_lt(ymin - xmax, z) | _fd_var_del_gt(ymax - xmin, z);
 
-      if (changed_z && fd_domain_empty(z))
-	return FD_NOSOLUTION;
+      if (changed_z)
+	{
+	  if (fd_domain_empty(z))
+	    return FD_NOSOLUTION;
+
+	  _fd_revise_connected(NULL, z);	// XXX: NULL or this?
+	}
     }
   else // culprit == z
     {
-      changed_x =
-	_fd_var_del_lt(_fd_var_min(y) - _fd_var_max(z), x);
-      changed_x |=
-	_fd_var_del_gt(_fd_var_max(y) - _fd_var_min(z), x);
+      zmin = _fd_var_min(z);
+      zmax = _fd_var_max(z);
 
-      if (changed_x && fd_domain_empty(x))
-	return FD_NOSOLUTION;
+      changed_x = _fd_var_del_lt(_fd_var_min(y) - zmax, x) |
+	_fd_var_del_gt(_fd_var_max(y) - zmin, x);
 
-      changed_y =
-	_fd_var_del_lt(_fd_var_min(x) + _fd_var_min(z), y);
-      changed_y |=
-	_fd_var_del_gt(_fd_var_max(x) + _fd_var_max(z), y);
+      if (changed_x)
+	{
+	  if (fd_domain_empty(x))
+	    return FD_NOSOLUTION;
 
-      if (changed_y && fd_domain_empty(y))
-	return FD_NOSOLUTION;
-    }
+	  _fd_revise_connected(NULL, x);	// XXX: NULL or this?
+	}
 
-  if (changed_x)
-    _fd_revise_connected(NULL, x);	// XXX: NULL or this?
+      xmin = _fd_var_min(x);
+      xmax = _fd_var_max(x);
 
-  if (changed_y)
-    _fd_revise_connected(NULL, y);	// XXX: NULL or this?
+      changed_y =
+	_fd_var_del_lt(xmin + zmin, y) | _fd_var_del_gt(xmax + zmax, y);
 
-  if (changed_z)
-    _fd_revise_connected(NULL, z);	// XXX: NULL or this?
+      if (changed_y)
+	{
+	  if (fd_domain_empty(y))
+	    return FD_NOSOLUTION;
 
-#if CONSTRAINT_TEMPS > 3
-  if (fd_var_single(x, 0) && fd_var_single(y, 0) && fd_var_single(z, 0))
+	  _fd_revise_connected(NULL, y);	// XXX: NULL or this?
+	}
+
+      ymin = zmin + xmin;
+      ymax = zmax + xmax;
+    }
+
+#ifndef DISABLE_ENTAILED
+  if ((xmin == xmax) + (ymin == ymax) == 2)
     fd__constraint_set_entailed(this);
 #endif
 
   return FD_OK;
 }
 
-int fd_var_eq_minus_propagate(fd_constraint this, fd_int revise)
+static int fd_var_eq_minus_propagate(fd_constraint this, fd_int revise)
 {
   fd_int x, y, z;
   int changed;
@@ -190,19 +230,15 @@ int fd_var_eq_minus_propagate(fd_constraint this, fd_int revise)
 
 fd_constraint fd_var_eq_minus(fd_int x, fd_int y, fd_int z)
 {
-  fd_constraint c = _fd_constraint_new(3, 0);
+  fd_constraint c = fd__constraint_new(3, 0);
 
   if (c)
     {
       c->variables[0] = FD_INT2C_VAR(x);
       c->variables[1] = FD_INT2C_VAR(y);
       c->variables[2] = FD_INT2C_VAR(z);
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_VAR_EQ_MINUS;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_var_eq_minus_propagate2;
-      c->propagator = fd_var_eq_minus_propagate;
-#endif /* CONSTRAINT_CLASS */
 
       _fd_var_add_constraint(x, c);
       _fd_var_add_constraint(y, c);
diff --git a/src/constraints/var-eq-times.c b/src/constraints/var-eq-times.c
index 17438f3..eedfd36 100644
--- a/src/constraints/var-eq-times.c
+++ b/src/constraints/var-eq-times.c
@@ -177,18 +177,15 @@ int fd_var_eq_times_propagate2(fd_constraint this, fd_int culprit)
 
 fd_constraint fd_var_eq_times(fd_int x, fd_int y, fd_int z)
 {
-  fd_constraint c = _fd_constraint_new(3, 0);
+  fd_constraint c = fd__constraint_new(3, 0);
 
   if (c)
     {
       c->variables[0] = FD_INT2C_VAR(x);
       c->variables[1] = FD_INT2C_VAR(y);
       c->variables[2] = FD_INT2C_VAR(z);
-#ifdef CONSTRAINT_CLASS
+
       c->kind = FD_CONSTR_VAR_EQ_TIMES;
-#else /* CONSTRAINT_CLASS */
-      c->propagator2 = fd_var_eq_times_propagate2;
-#endif /* CONSTRAINT_CLASS */
 
       _fd_var_add_constraint(x, c);
       _fd_var_add_constraint(y, c);
diff --git a/src/dsearch-sg.c b/src/dsearch-sg.c
index 55b1886..1a7f761 100644
--- a/src/dsearch-sg.c
+++ b/src/dsearch-sg.c
@@ -15,6 +15,9 @@
 #include "variables.h"
 #include "values.h"
 #include "bound.h"
+#include "constraints.h"
+
+#include "util.h"
 
 #if STEAL_WORK > 0
 #include <time.h>
@@ -52,9 +55,7 @@ void _fd_epoch_forward()
 
 void _fd_epoch_rewind()
 {
-#ifdef CONSTRAINT_TEMPS
   fd__constraint_data_reset();
-#endif
 }
 
 int _fd_dsearch(fd_int variables[], int agent_no)
@@ -69,20 +70,20 @@ int _fd_dsearch(fd_int variables[], int agent_no)
   agent = agent_no;
 
 #if defined(NEW_ENTRANCE) && defined(DEBUG_SPLITGO)
-  _fd_debug("[%d.%d] agents_searching = %d, agents_failed = %d\n", tid, agent, agents_searching, agents_failed);
+  fd__debug("[%d.%d] agents_searching = %d, agents_failed = %d\n", tid, agent, agents_searching, agents_failed);
 #endif
 
   _fd_init_local_depository();
 
 #if DEBUG_SPLITGO > 1
-  _fd_output("[%d.%d] ", tid, agent); _fd_cprint2(variables);
+  fd__output("[%d.%d] ", tid, agent); _fd_cprint2(variables);
 #endif
 
   if (_fd_filter_domains() == FD_NOSOLUTION)
     {
       fd_int variable = NULL;
 
-      _fd_debug("[%d.%d] failed before beginning\n", tid, agent);
+      fd__trace("[%d.%d] failed before beginning\n", tid, agent);
 
 #if STEAL_WORK > 0
 #ifdef NEW_ENTRANCE
@@ -106,20 +107,14 @@ int _fd_dsearch(fd_int variables[], int agent_no)
 
       do
 	{
-#ifndef INDEX_IN_POOL
-	  if (!_fd_steal_store(store, agent_no, 7))
-#else
 	  if (!_fd_steal_store(store, &variable, agent_no, 7))
-#endif
 	    {
-	      _fd_debug("[%d.%d] giving up\n", tid, agent);
+	      fd__trace("[%d.%d] giving up\n", tid, agent);
 
 	      return FD_NOSOLUTION;
 	    }
 
-#ifdef CONSTRAINT_TEMPS
 	  fd__constraint_data_reset();	// XXX: this doesn't belong here!
-#endif
 	}
       while (_fd_revise_wrt_variable(variable) == FD_NOSOLUTION);
 #else  /* STEAL_WORK > 0 */
@@ -128,7 +123,7 @@ int _fd_dsearch(fd_int variables[], int agent_no)
     }
 
 #if DEBUG_SPLITGO > 1
-  _fd_output("[%d.%d] ", tid, agent); _fd_cprint2(variables);
+  fd__output("[%d.%d] ", tid, agent); _fd_cprint2(variables);
 #endif
 
 #ifdef NEW_ENTRANCE
@@ -139,7 +134,7 @@ int _fd_dsearch(fd_int variables[], int agent_no)
   pthread_spin_unlock(&entrance_lock);
 #endif
 
-  _fd_debug("[%d.%d] about to start searching\n", tid, agent);
+  fd__trace("[%d.%d] about to start searching\n", tid, agent);
 
   return _fd_dsearch_(variables);
 }
@@ -170,7 +165,7 @@ static int _fd_dsearch_(fd_int _fd_variables[])
       instantiations++;
 #ifdef DEBUG_PROGRESS
       if (instantiations % 100000 == 0)
-	_fd_debug("[%d.%d] still alive (%d stores)\n", tid, agent,
+	fd__debug("[%d.%d] still alive (%d stores)\n", tid, agent,
 		  next_split_index - base_split_index);
 #endif
 
@@ -202,7 +197,7 @@ static int _fd_dsearch_(fd_int _fd_variables[])
 		{
 		  if (!_fd_counting_solutions)
 		    {
-		      _fd_debug("[%d.%d] performed %d instantiations and %d backtracks\n",
+		      fd__trace("[%d.%d] performed %d instantiations and %d backtracks\n",
 				tid, agent, instantiations, backtrack);
 
 		      _fd_statistics_pool();
@@ -229,10 +224,10 @@ static int _fd_dsearch_(fd_int _fd_variables[])
 
   if (!_fd_counting_solutions)
     {
-      _fd_debug("[%d.%d] performed %d instantiations and %d backtracks\n", tid,
+      fd__trace("[%d.%d] performed %d instantiations and %d backtracks\n", tid,
 		agent, instantiations, backtrack);
 
-      _fd_debug("[%d.%d] %d stores in store\n", tid, agent, next_split_index - base_split_index);
+      fd__trace("[%d.%d] %d stores in store\n", tid, agent, next_split_index - base_split_index);
 
       _fd_statistics_pool();
     }
diff --git a/src/fdc_int.h b/src/fdc_int.h
index 57246b9..d12eda2 100644
--- a/src/fdc_int.h
+++ b/src/fdc_int.h
@@ -152,33 +152,15 @@ struct fd_value {
 #define FD_INT2C_VAR(v) ((v)->index)
 
 struct fd_constraint {
-#ifdef CONSTRAINT_TEMPS
+#if defined(CONSTRAINT_TEMPS) || !defined(DISABLE_ENTAILED)
   int	index;
 #endif
   C_VAR_T *variables;	  // variables involved in the constraint
   int    nvariables;	  // number of variables
   int    *constants;	  // constraint's constants (where applicable)
   int    nconstants;	  // number of constants
-#ifdef CONSTRAINT_CLASS
 //  _fd_constraint_kind kind;	// what kind of constraint is this?
   int    kind;		  // what kind of constraint is this? // XXX
-#else /* CONSTRAINT_CLASS */
-  int    (*propagator2)(fd_constraint, fd_int);
-                          // how to propagate changes in a variable's
-                          // domain; 2nd argument is the variable
-                          // against which to revise the domains of
-                          // the other variables in the constraint;
-                          // returns 1 if some domain becomes empty
-  int    (*filter)(fd_constraint);
-                          // performs an initial filtering of the
-			  // domains of the constraint variables
-  int    (*propagator)(fd_constraint, fd_int);
-                          // how to propagate changes in a variable's
-                          // domain; 2nd argument is the variable to
-                          // revise (against the domains of every
-                          // other variable in the constraint);
-                          // returns 1 if some domain became empty
-#endif /* CONSTRAINT_CLASS */
 };
 
 #define fd_cvariable(c,v) ((c)->variables[v]) // XXX: not being used
@@ -197,6 +179,7 @@ extern int fd_variables_count;
 extern __thread fd_int _fd_variables[];
 
 fd_int fd_new(int, int);
+fd_int fd_const(int);
 
 // type for the variable's constraints
 #define VAR_C_T int
@@ -266,6 +249,8 @@ typedef enum {
 
 #endif /* DISTRIBUTED_SOLVER */
 
-#ifdef FAST
-#  define _fd_debug(...) ((void) 0)
-#endif /* FAST */
+#ifdef TRACE
+#  define fd__trace fd__debug
+#else
+#  define fd__trace(...) ((void) 0)
+#endif
diff --git a/src/matching.c b/src/matching.c
index 9fbf099..13a8933 100644
--- a/src/matching.c
+++ b/src/matching.c
@@ -9,6 +9,10 @@
 #include "values.h"
 #include "matching.h"
 
+#ifdef DEBUG_MATCH
+#  include "util.h"
+#endif
+
 #define UNSEEN (-2)		// value not seen in any domain
 
 #define WHITE  (-1)
@@ -297,7 +301,7 @@ static void _fd_mark_cycles(fd_constraint c, int8_t *vgraph, int r_verts,
 
 	      if (vgraph[var * r_verts + val] == 1)
 		{
-//		  _fd_debug("marking %d in %d's domain\n", val + min, VAR(c, var)->index);
+//		  fd__debug("marking %d in %d's domain\n", val + min, VAR(c, var)->index);
 
 		  vgraph[var * r_verts + val] = 3;
 	      }
@@ -604,7 +608,7 @@ static void _fd_remark_cycles(fd_constraint c, int culprit, int8_t *vgraph,
 
 	      if (vgraph[var * r_verts + val] == 1)
 		{
-//		  _fd_debug("marking %d in %d's domain\n", val + min, VAR(c, var)->index);
+//		  fd__debug("marking %d in %d's domain\n", val + min, VAR(c, var)->index);
 
 		  vgraph[var * r_verts + val] = 3;
 	      }
@@ -620,7 +624,7 @@ static void _fd_remark_cycles(fd_constraint c, int culprit, int8_t *vgraph,
       for (i = 0; i < nvariables; ++i)
 	if (l_scc[i] == r_scc[v] && vgraph[i * r_verts + v] == 1)
 	  {
-//	    _fd_debug("marking %d in %d's domain\n", v + min, VAR(c, i)->index);
+//	    fd__debug("marking %d in %d's domain\n", v + min, VAR(c, i)->index);
 
 	    vgraph[i * r_verts + v] = 3;
 	  }
@@ -694,7 +698,7 @@ static void _fd_mark_paths(fd_constraint c, int8_t *vgraph, int r_verts,
 			continue;
 
 #if DEBUG_MATCH > 1
-		      _fd_debug("marking %d in %d's domain\n",
+		      fd__debug("marking %d in %d's domain\n",
 				queue[head] + min, VAR(c, v)->index);
 #endif
 		      // this edge is in an M-alternating path
@@ -772,7 +776,7 @@ int _fd_find_matching(fd_constraint c, int **memory)
   if (*memory == NULL)
     {
 #ifdef DEBUG_MATCH
-      _fd_debug("all-different memory size is %d\n", size);
+      fd__debug("all-different memory size is %d\n", size);
 #endif
 
       *memory = malloc(size);		// XXX: NULL
@@ -784,7 +788,7 @@ int _fd_find_matching(fd_constraint c, int **memory)
     {
       // the allocated memory is not enough
 #ifdef DEBUG_MATCH
-      _fd_debug("all-different new memory size is %d\n", size);
+      fd__debug("all-different new memory size is %d\n", size);
 #endif
 
       *memory = realloc(*memory, size);	// XXX: NULL
@@ -808,13 +812,13 @@ int _fd_find_matching(fd_constraint c, int **memory)
   l_scc = r_edge + r_verts;
   r_scc = l_scc + nvariables;
   vgraph = (int8_t *) (r_scc + r_verts);
-#else
+#else /* CONSTRAINT_TEMPS */
   l_edge = alloca(nvariables * sizeof(*l_edge));
   r_edge = alloca(r_verts * sizeof(*r_edge));
   l_scc = alloca(nvariables * sizeof(*l_scc));
   r_scc = alloca(r_verts * sizeof(*r_scc));
   vgraph = alloca(nvariables * r_verts * sizeof(*vgraph));
-#endif
+#endif /* CONSTRAINT_TEMPS */
 
   l_parent = alloca(nvariables * sizeof(*l_parent));
   l_colour = alloca(nvariables * sizeof(*l_colour));
@@ -967,7 +971,7 @@ int _fd_find_matching(fd_constraint c, int **memory)
 	if (vgraph[i * r_verts + v] == 1)
 	  {
 #ifdef DEBUG_MATCH
-	    _fd_debug("removing %d from %d's domain\n", v + min,
+	    fd__debug("removing %d from %d's domain\n", v + min,
 		      VAR(c, i)->index);
 #endif
 	    changed |= _fd_var_del_val(v + min, VAR(c, i));
@@ -1198,7 +1202,7 @@ int _fd_update_matching(fd_constraint c, fd_int culprit, int *memory)
 	if (vgraph[i * r_verts + v] == 1 && /* {XXX */ r_scc[v] == -1 /* XXX} */)
 	  {
 #ifdef DEBUG_MATCH
-	    _fd_debug("removing %d from %d's domain\n", v + min,
+	    fd__debug("removing %d from %d's domain\n", v + min,
 		      VAR(c, i)->index);
 #endif
 	    changed |= _fd_var_del_val(v + min, VAR(c, i));
diff --git a/src/options.c b/src/options.c
index 713f929..5b74778 100644
--- a/src/options.c
+++ b/src/options.c
@@ -117,7 +117,7 @@ static char *_fd_options = "@(#) $Options: "
 #endif
 
 #ifdef CONSTRAINT_CLASS
-			   "CONSTRAINT_CLASS" " "
+#warning "CONSTRAINT_CLASS committed (16JUN16)"
 #endif
 #ifdef CONSTRAINT_TEMPS
 			   "CONSTRAINT_TEMPS" " "
@@ -143,7 +143,7 @@ static char *_fd_options = "@(#) $Options: "
 			   "GROWABLE_POOL" " "
 #endif
 #ifdef INDEX_IN_POOL
-			   "INDEX_IN_POOL" " "
+#warning "INDEX_IN_POOL committed (24APR16)"
 #endif
 #ifdef STORE_IN_POOL
 			   "STORE_IN_POOL" " "
diff --git a/src/paccs.h b/src/paccs.h
index 88cd5ad..b1312bf 100644
--- a/src/paccs.h
+++ b/src/paccs.h
@@ -14,6 +14,7 @@ void fd_init(int *argc, char **argv[]);
 void fd_end(void);
 int fd_solve(void);
 
+fd_int fd_const(int value);
 fd_int fd_new(int min, int max);
 
 void fd_label(fd_int vars[], int nvars);
@@ -40,6 +41,7 @@ fd_constraint fd_var_eq_times(fd_int x, fd_int y, fd_int z);
 /* Global constraints */
 
 fd_constraint fd_all_different(fd_int X[], int n);
+fd_constraint fd_fake_all_different(fd_int X[], int n);
 
 fd_constraint fd_element(fd_int X[], int n, fd_int y, int k);
 fd_constraint fd_element_var(fd_int X[], int n, fd_int y, fd_int z);
@@ -51,6 +53,7 @@ fd_constraint fd_sum_prod(fd_int X[], fd_int Y[], int n, int k);
 
 fd_constraint fd_poly_eq(int C[], fd_int Y[], int n, fd_int z);
 fd_constraint fd_poly_eq_k(int C[], fd_int Y[], int n, int k);
+fd_constraint fd_poly_le_k(int C[], fd_int Y[], int n, int k);
 fd_constraint fd_poly_ne(int C[], fd_int Y[], int n, fd_int y);
 fd_constraint fd_poly_ne_k(int C[], fd_int Y[], int n, int k);
 fd_constraint fd_knapsack2(fd_int X[], fd_int Y[], int n, fd_int z);
diff --git a/src/packed.c b/src/packed.c
index 43d858b..cfb7f81 100644
--- a/src/packed.c
+++ b/src/packed.c
@@ -11,6 +11,8 @@
 #include "constraints.h"
 #include "packed.h"
 
+#include "util.h"
+
 static struct _fd_packed_problem *packed_memory; // XXX: so it can be freed
 #ifdef USE_MMAP
 static size_t packed_memory_size;
@@ -42,7 +44,7 @@ static struct _fd_packed_problem *_fd_allocate_memory()
       tconsts += _fd_constraints[i]->nconstants;
     }
 
-  _fd_debug("packed size is %d + %d + %d + %d\n",
+  fd__trace("packed size is %d + %d + %d + %d\n",
 	    sizeof(*p),
 	    p->nvariables * sizeof(*p->variables),
 	    p->nconstraints * sizeof(*p->constraints),
@@ -86,7 +88,7 @@ static struct _fd_packed_problem *_fd_allocate_memory()
       {
 	perror("mmap");
 
-	_fd_fatal("mmap() failed");
+	fd__fatal("mmap() failed");
       }
 
     p = m;
@@ -141,10 +143,6 @@ void _fd_pack_problem()
   int offset;
   int i, j;
 
-#ifndef CONSTRAINT_CLASS
-  _fd_fatal("packed problems require CONSTRAINT_CLASS");
-#endif
-
   packed = _fd_allocate_memory();
 
   assert(packed != NULL); // XXX
diff --git a/src/packed.h b/src/packed.h
index 8c1654c..bc65f82 100644
--- a/src/packed.h
+++ b/src/packed.h
@@ -36,7 +36,7 @@ struct _fd_packed_int {
 
 /* fields must correspond to an initial segment of fd_constraint */
 struct _fd_packed_constraint {
-#ifdef CONSTRAINT_TEMPS
+#if defined(CONSTRAINT_TEMPS) || !defined(DISABLE_ENTAILED)
   int	  index;
 #endif
   C_VAR_T *variables;
diff --git a/src/pool.c b/src/pool.c
index 18e05f3..fae7b59 100644
--- a/src/pool.c
+++ b/src/pool.c
@@ -10,12 +10,9 @@ static __thread _fd_store alt_store;  // the other side of the split
 static _fd_store *split_stores_;
 #define split_stores split_stores_[agent]
 
-#ifndef INDEX_IN_POOL
-static __thread fd_int *split_variable;
-#else
 static int **split_variable_;
-static __thread int *split_variable;
-#endif
+static __thread int *split_variable;	// (index of the) variable whose domain
+					// was split to obtain the store
 
 #ifdef GROWABLE_POOL
 // number of stores in the pool
@@ -100,9 +97,7 @@ void _fd_init_store_depository(int agents)
   for (i = 0; i < agents; ++i)
     split_indexes[i] = EMPTY_POOL;
 
-#ifdef INDEX_IN_POOL
   split_variable_ = calloc(agents, sizeof(*split_variable_));
-#endif
 
   stores_mutexes = calloc(agents, sizeof(*stores_mutexes));
   for (i = 0; i < agents; ++i)
@@ -130,18 +125,15 @@ void _fd_init_store_depository(int agents)
 
 static void _fd_init_local_depository()
 {
-  if (split_stores)
+  if (split_stores)	// see if it has already been initialised
     {
 #ifdef STORE_IN_POOL
       // copy the new store to the pool
       store = split_stores;
       memcpy(store, main_store, STORE_SIZE);
-#ifdef INDEX_IN_POOL
-      split_variable[next_split_index] = -1;
-#else
-      split_variable[next_split_index] = NULL;
-#endif
-      ++next_split_index;
+
+      split_variable[next_split_index] = -1;	// starting anew
+      next_split_index++;
 #endif /* STORE_IN_POOL */
 
       return;
@@ -149,24 +141,24 @@ static void _fd_init_local_depository()
 
 #ifndef GROWABLE_POOL
 #ifndef STORE_IN_POOL
-  _fd_debug("[%d.%d] stores size %d + %d\n", tid, agent,
+  fd__trace("[%d.%d] stores size %d + %d\n", tid, agent,
 	    fd_variables_count * STORE_SIZE,
 	    fd_variables_count * sizeof(*split_variable));
   if (posix_memalign((void **) &split_stores, sysconf(_SC_PAGESIZE),
 		     fd_variables_count * STORE_SIZE +
 		     fd_variables_count * sizeof(*split_variable)))
-    _fd_fatal("unable to allocate pool memory");
+    fd__fatal("unable to allocate pool memory");
 
   // XXX: alignment problems possible
   split_variable = ((void *) split_stores) + fd_variables_count * STORE_SIZE;
 #else
-  _fd_debug("[%d.%d] stores size %d + %d\n", tid, agent,
+  fd__trace("[%d.%d] stores size %d + %d\n", tid, agent,
 	    (fd_variables_count + 1) * STORE_SIZE,
 	    fd_variables_count * sizeof(*split_variable));
   if (posix_memalign((void **) &split_stores, sysconf(_SC_PAGESIZE),
 		     (fd_variables_count + 1) * STORE_SIZE +
 		     fd_variables_count * sizeof(*split_variable)))
-    _fd_fatal("unable to allocate pool memory");
+    fd__fatal("unable to allocate pool memory");
 
   // XXX: alignment problems possible
   split_variable = ((void *) split_stores) + (fd_variables_count + 1) * STORE_SIZE;
@@ -180,21 +172,19 @@ static void _fd_init_local_depository()
     fd__label_vars_count + 1 : POOL_DEFAULT_SIZE;
 #endif
 
-  _fd_debug("[%d.%d] stores size %d + %d\n", tid, agent, pool_size * STORE_SIZE,
+  fd__trace("[%d.%d] stores size %d + %d\n", tid, agent, pool_size * STORE_SIZE,
 	    fd_variables_count * sizeof(*split_variable));
-	 
+
   if (posix_memalign((void **) &split_stores, sysconf(_SC_PAGESIZE),
 		     pool_size * STORE_SIZE))
-    _fd_fatal("unable to allocate pool memory");
+    fd__fatal("unable to allocate pool memory");
 
   if (posix_memalign((void **) &split_variable, sysconf(_SC_PAGESIZE),
 		     fd_variables_count * sizeof(*split_variable)))
-    _fd_fatal("unable to allocate pool variable memory");
+    fd__fatal("unable to allocate pool variable memory");
 #endif /* GROWABLE_POOL */
 
-#if defined(INDEX_IN_POOL) && !defined(split_variable)
   split_variable_[agent] = split_variable;
-#endif
 
   local_split_indexes = calloc(POOL_INDEXES, sizeof(*local_split_indexes));
   split_indexes[agent] = local_split_indexes;
@@ -204,12 +194,8 @@ static void _fd_init_local_depository()
   main_store = store;
   store = split_stores;
   memcpy(store, main_store, STORE_SIZE);
-#ifdef INDEX_IN_POOL
   split_variable[next_split_index] = -1;
-#else
-  split_variable[next_split_index] = NULL;
-#endif
-  ++next_split_index;
+  next_split_index++;
 #endif /* STORE_IN_POOL */
 }
 
@@ -225,11 +211,11 @@ static void _fd_grow_pool(void)
 #else
   nsize = (fd_variables_count + 1 < nsize) ? fd_variables_count + 1 : nsize;
 #endif
-  _fd_debug("[%d.%d] new pool size is %d\n", tid, agent, nsize * STORE_SIZE);
-	 
+  fd__trace("[%d.%d] new pool size is %d\n", tid, agent, nsize * STORE_SIZE);
+
   if (posix_memalign((void **) &npool, sysconf(_SC_PAGESIZE),
 		     nsize * STORE_SIZE))
-    _fd_fatal("unable to allocate pool memory");
+    fd__fatal("unable to allocate pool memory");
 
   memcpy(npool, split_stores, pool_size * STORE_SIZE);
 
@@ -251,7 +237,7 @@ static void _fd_grow_pool(void)
 }
 #endif /* GROWABLE_POOL */
 
-/* 
+/*
    saving a store to the pool is done in two steps:
 
    1. a copy of the store which is about to be split is saved (to have
@@ -280,30 +266,18 @@ static void _fd_start_saving_store(_fd_store *dst)
 static void _fd_end_saving_store(fd_int variable)
 {
 #ifndef STORE_IN_POOL
-#ifndef INDEX_IN_POOL
-  split_variable[next_split_index] = variable;
-#else
   split_variable[next_split_index] = variable->index;
-#endif
 #else /* STORE_IN_POOL */
   // here, the split also affects the store which is below the
   // (about to be) current one
-#ifndef INDEX_IN_POOL
-  split_variable[next_split_index - 1] = variable;
-#else
   split_variable[next_split_index - 1] = variable->index;
-#endif
 #endif /* STORE_IN_POOL */
 
-  ++next_split_index;
+  next_split_index++;
 }
 
 // try to steal a store from another agent
-#ifndef INDEX_IN_POOL
-bool _fd_steal_store(_fd_store store_, int agent, int retries)
-#else
 bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
-#endif
 {
 #ifndef RANDOM_VICTIM
   int v;		// the victim
@@ -317,7 +291,7 @@ bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
 
 #if defined(STORE_IN_POOL) && defined(DECREMENT_EARLY)
   if (agent != -1)
-    --next_split_index;
+    next_split_index--;
 #endif
 
   // only let one agent in at a time
@@ -381,7 +355,7 @@ bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
 	      v = candidate;
 
 	      if (retrying)
-		_fd_debug("[%d.%d] %d tries left\n", tid, agent, retries);
+		fd__trace("[%d.%d] %d tries left\n", tid, agent, retries);
 
 	      break;
 	    }
@@ -392,10 +366,10 @@ bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
 
 	  if (active == 0 || retries == 0)
 	    {
-	      _fd_debug("[%d.%d] no agent can supply work\n", tid, agent);
+	      fd__trace("[%d.%d] no agent can supply work\n", tid, agent);
 
 	      if (retrying)
-		_fd_debug("[%d.%d] %d tries left\n", tid, agent, retries);
+		fd__trace("[%d.%d] %d tries left\n", tid, agent, retries);
 
 	      return false;
 	    }
@@ -429,13 +403,13 @@ bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
   // lock access to victim's stores
   pthread_mutex_lock(&stores_mutexes[v]);
 #if DEBUG_STEALING > 1
-  _fd_debug("[%d.%d] locked %d stores' mutex\n", tid, agent, v);
+  fd__debug("[%d.%d] locked %d stores' mutex\n", tid, agent, v);
 #endif
 
   stores = split_indexes[v][1] - split_indexes[v][0];
 
 #if DEBUG_STEALING > 1
-  _fd_debug("[%d.%d] %d stores left at %d\n", tid, agent, stores, v);
+  fd__debug("[%d.%d] %d stores left at %d\n", tid, agent, stores, v);
 #endif
 
   // don't steal the last few stores from an agent
@@ -445,7 +419,7 @@ bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
       pthread_mutex_unlock(&thieves_mutex);
 
 #ifdef DEBUG_STEALING
-      _fd_debug("[%d.%d] not enough stores (%d) at %d\n", tid, agent, stores, v);
+      fd__debug("[%d.%d] not enough stores (%d) at %d\n", tid, agent, stores, v);
 #endif
 
       return false;
@@ -463,14 +437,12 @@ bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
 
   memcpy(store_, &split_stores_[v][s * fd_variables_count], STORE_SIZE);
 
-#ifdef INDEX_IN_POOL
 #ifndef STORE_IN_POOL
   if (variable)
 #else
   if (variable && split_variable_[v][s] != -1)
 #endif
     *variable = _fd_variables[split_variable_[v][s]];
-#endif
 
   // this agent will likely become the one with the largest search space
   // (if work is being stolen by the controller, most_work will be set
@@ -490,14 +462,10 @@ bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
     }
 
 #ifdef DEBUG_STEALING
-#ifdef INDEX_IN_POOL
-  _fd_debug("[%d.%d] stole work (%d/%d) from %d (var %d)\n", tid, agent, s,
+  fd__debug("[%d.%d] stole work (%d/%d) from %d (var %d)\n", tid, agent, s,
 	    stores, v, split_variable_[v][s]);
-#else
-  _fd_debug("[%d.%d] stole work (%d/%d) from %d\n", tid, agent, s, stores, v);
-#endif
 #if DEBUG_STEALING > 2
-  _fd_output("[%d.%d] ", tid, agent); _fd_cprint3(store_);
+  fd__output("[%d.%d] ", tid, agent); _fd_cprint3(store_);
 #endif
 #endif
 
@@ -523,7 +491,7 @@ bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
 
 #if defined(STORE_IN_POOL) && defined(DECREMENT_EARLY)
   if (agent != -1)
-    --next_split_index;
+    next_split_index--;
 #endif
 
   pthread_spin_lock(&starving_lock);
@@ -535,7 +503,7 @@ bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
   if (as == nagents)
     {
 #ifdef DEBUG_STEALING
-      _fd_debug("[%d.%d] all agents starving...\n", tid, agent);
+      fd__debug("[%d.%d] all agents starving...\n", tid, agent);
 #endif
 
       return false;
@@ -565,14 +533,14 @@ bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
 	  // lock access to victim's stores ...
 	  pthread_mutex_lock(&stores_mutexes[v]);
 #if DEBUG_STEALING > 1
-	  _fd_debug("[%d.%d] locked %d stores' mutex\n", tid, agent, v);
+	  fd__debug("[%d.%d] locked %d stores' mutex\n", tid, agent, v);
 #endif
 
 	  // ... and make sure that the victim has enough stores
 	  stores = split_indexes[v][1] - split_indexes[v][0];
 
 #if DEBUG_STEALING > 1
-	  _fd_debug("[%d.%d] %d stores left at %d\n", tid, agent, stores, v);
+	  fd__debug("[%d.%d] %d stores left at %d\n", tid, agent, stores, v);
 #endif
 
 	  // don't steal the last few stores from an agent
@@ -583,7 +551,7 @@ bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
 	  pthread_mutex_unlock(&stores_mutexes[v]);
 
 #ifdef DEBUG_STEALING
-	  _fd_debug("[%d.%d] not enough stores (%d) at %d\n", tid, agent,
+	  fd__debug("[%d.%d] not enough stores (%d) at %d\n", tid, agent,
 		    stores, v);
 #endif
 	}
@@ -596,7 +564,7 @@ bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
 	  // the main thread won't keep trying
 	  if (agent == -1)
 	    {
-	      _fd_debug("[%d] giving up stealing\n", tid);
+	      fd__trace("[%d] giving up stealing\n", tid);
 
 	      return false;
 	    }
@@ -608,7 +576,7 @@ bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
 	  if (as == nagents)
 	    {
 #ifdef DEBUG_STEALING
-	      _fd_debug("[%d.%d] all agents starving...\n", tid, agent);
+	      fd__debug("[%d.%d] all agents starving...\n", tid, agent);
 #endif
 
 	      return false;
@@ -619,7 +587,7 @@ bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
 #endif
 
 #ifdef DEBUG_STEALING
-	  _fd_debug("[%d.%d] pausing...\n", tid, agent);
+	  fd__debug("[%d.%d] pausing...\n", tid, agent);
 #endif
 
 	  nanosleep(&delay, NULL);
@@ -631,7 +599,7 @@ bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
 	  if (as == nagents)
 	    {
 #ifdef DEBUG_STEALING
-	      _fd_debug("[%d.%d] all agents starving...\n", tid, agent);
+	      fd__debug("[%d.%d] all agents starving...\n", tid, agent);
 #endif
 
 	      return false;
@@ -667,14 +635,12 @@ bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
 
   memcpy(store_, &split_stores_[v][s * fd_variables_count], STORE_SIZE);
 
-#ifdef INDEX_IN_POOL
 #ifndef STORE_IN_POOL
   if (variable)
 #else
   if (variable && split_variable_[v][s] != -1)
 #endif
     *variable = _fd_variables[split_variable_[v][s]];
-#endif
 
   // this agent will likely become the one with the largest search space
   // (if work is being stolen by the controller, most_work will be set
@@ -697,14 +663,10 @@ bool _fd_steal_store(_fd_store store_, fd_int *variable, int agent, int retries)
     }
 
 #if DEBUG_STEALING > 0
-#ifdef INDEX_IN_POOL
-  _fd_debug("[%d.%d] stole work (%d/%d) from %d (var %d)\n", tid, agent, s,
+  fd__debug("[%d.%d] stole work (%d/%d) from %d (var %d)\n", tid, agent, s,
 	    stores, v, split_variable_[v][s]);
-#else
-  _fd_debug("[%d.%d] stole work (%d/%d) from %d\n", tid, agent, s, stores, v);
-#endif
 #if DEBUG_STEALING > 2
-  _fd_output("[%d.%d] ", tid, agent); _fd_cprint3(store_);
+  fd__output("[%d.%d] ", tid, agent); _fd_cprint3(store_);
 #endif
 #endif
 
@@ -733,24 +695,20 @@ static bool _fd_restore_store(fd_int *variable)
 #if STEAL_WORK < 1
     return false;
 #else /* STEAL_WORK < 1 */
-#ifndef INDEX_IN_POOL
-    return _fd_steal_store(store, agent, 0);
-#else
     return _fd_steal_store(store, variable, agent, 0);
-#endif
 #endif /* STEAL_WORK < 1 */
 
 #if STEAL_WORK > 0
   if (next_split_index - base_split_index < INDEX_SAFE)
     {
-      //_fd_debug("[%d.%d] have %d stores\n", tid, agent, next_split_index - base_split_index);
-#ifndef FAST
+      //fd__trace("[%d.%d] have %d stores\n", tid, agent, next_split_index - base_split_index);
+#ifdef TRACE
       if (pthread_mutex_trylock(&stores_mutexes[agent]))
-	_fd_debug("[%d.%d] *** found stores locked [%d,%d[\n", tid, agent,
+	fd__trace("[%d.%d] *** found stores locked [%d,%d[\n", tid, agent,
 		  base_split_index, next_split_index),
 #endif
       pthread_mutex_lock(&stores_mutexes[agent]);
-      //_fd_debug("[%d.%d] locked stores\n", tid, agent);
+      //fd__trace("[%d.%d] locked stores\n", tid, agent);
 
       locked = true;
     }
@@ -763,7 +721,7 @@ static bool _fd_restore_store(fd_int *variable)
   ++pool_gets;
 #endif
 
-  --next_split_index;
+  next_split_index--;
 
 #ifndef STORE_IN_POOL
   memcpy(store, &split_stores[next_split_index * fd_variables_count],
@@ -774,18 +732,10 @@ static bool _fd_restore_store(fd_int *variable)
 #endif
 
 #ifndef STORE_IN_POOL
-#  ifndef INDEX_IN_POOL
-  *variable = split_variable[next_split_index];
-#  else
   *variable = _fd_variables[split_variable[next_split_index]];
-#  endif /* INDEX_IN_POOL */
 #else
-#  ifndef INDEX_IN_POOL
-  *variable = split_variable[next_split_index - 1];
-#  else
   assert(split_variable[next_split_index - 1] != -1);
   *variable = _fd_variables[split_variable[next_split_index - 1]];
-#  endif /* INDEX_IN_POOL */
 #endif /* STORE_IN_POOL */
 
 #if STEAL_WORK > 0
diff --git a/src/problem.c b/src/problem.c
index 4b51163..30d0330 100644
--- a/src/problem.c
+++ b/src/problem.c
@@ -10,6 +10,8 @@
 #include "values.h"
 #include "constraints.h"
 
+#include "util.h"
+
 extern int fd__workers;
 
 static void _fd_cleanup(void);
@@ -114,7 +116,7 @@ void fd_init(int *argc, char **argv[])
 {
 #ifdef SPLITGO_MPI
   if (MPI_Init(argc, argv))
-    _fd_fatal("MPI_Init failed");
+    fd__fatal("MPI_Init failed");
 #endif
 
   _fd_parse_general_options(argc, *argv);
@@ -123,9 +125,7 @@ void fd_init(int *argc, char **argv[])
   fd__init_splitgo(argc, *argv);
 #endif
 
-#ifdef CONSTRAINT_CLASS
-  _fd_init_constraints();
-#endif
+  fd__init_constraints();
 
 #ifdef USE_STORE
   _fd_init_main_store();
diff --git a/src/revisions.c b/src/revisions.c
index 0f396ed..1243d90 100644
--- a/src/revisions.c
+++ b/src/revisions.c
@@ -7,6 +7,8 @@
 #include "fdc_int.h"
 #include "constraints.h"
 
+#include "util.h"
+
 #ifdef REVISION_IS_VAR
 typedef fd_int revision;
 #else /* REVISION_IS_VAR */
@@ -181,7 +183,7 @@ int _fd_perform_revisions()
 	  if (_fd_propagate(c, v) == FD_NOSOLUTION)
 	    {
 #ifdef COUNT_REVISIONS
-	      _fd_debug("failed after %d revisions\n", nr + 1);
+	      fd__debug("failed after %d revisions\n", nr + 1);
 #endif
 
 	      return FD_NOSOLUTION;
@@ -212,7 +214,7 @@ int _fd_perform_revisions()
 	      free(r);
 
 #ifdef COUNT_REVISIONS
-	      _fd_debug("failed after %d revisions\n", nr + 1);
+	      fd__debug("failed after %d revisions\n", nr + 1);
 #endif
 
 	      return FD_NOSOLUTION;
@@ -228,7 +230,7 @@ int _fd_perform_revisions()
 #endif /* ORDER_REVISIONS */
 
 #ifdef COUNT_REVISIONS
-  _fd_debug("performed %d revisions\n", nr);
+  fd__debug("performed %d revisions\n", nr);
 #endif
 
   return FD_OK;
@@ -251,7 +253,7 @@ static int filter_domains()
       if (_fd_filter(c) == FD_NOSOLUTION)
 	{
 #ifdef COUNT_REVISIONS
-	  _fd_debug("failed after %d filtering steps\n", nr + 1);
+	  fd__debug("failed after %d filtering steps\n", nr + 1);
 #endif
 
 	  return FD_NOSOLUTION;
@@ -263,7 +265,7 @@ static int filter_domains()
     }
 
 #ifdef COUNT_REVISIONS
-  _fd_debug("performed %d filtering steps\n", nr);
+  fd__debug("performed %d filtering steps\n", nr);
 #endif
 
   // revise domains wrt the variables whose domains changed
@@ -272,9 +274,7 @@ static int filter_domains()
 
 int _fd_filter_domains()
 {
-#ifdef CONSTRAINT_TEMPS
   fd__constraint_data_reset();
-#endif
 
   return filter_domains();
 }
diff --git a/src/splitting.c b/src/splitting.c
index 0b5ff2e..c807cbb 100644
--- a/src/splitting.c
+++ b/src/splitting.c
@@ -5,6 +5,8 @@
 #include "values.h"
 #include "store.h"
 
+#include "util.h"
+
 #ifndef COMPACT_DOMAINS
 #error "only works with COMPACT_DOMAINS"
 #endif
@@ -210,7 +212,7 @@ static int fd__split_even_one(int n, _fd_store stores[])
 
   if (v == fd__label_vars_count)
     {
-      _fd_debug("[%d] cannot split %d-ways evenly, resorting to even "
+      fd__debug("[%d] cannot split %d-ways evenly, resorting to even "
 		"splitting\n", tid, n);
 
       return fd__split_even(n, stores);
@@ -258,7 +260,7 @@ int fd__split_problem(int n, _fd_store stores[],
   int v, p;
 
 #ifdef DEBUG_SPLITTING
-  _fd_output("before splitting: "); _fd_cprint3(store);
+  fd__output("before splitting: "); _fd_cprint3(store);
 #endif
 
   parts = splitter(n, stores);
@@ -272,9 +274,9 @@ int fd__split_problem(int n, _fd_store stores[],
 	  _fd_copy_value(SVALUE(stores[p][v]), DOMAIN(_fd_variables[v]));
 
 #ifdef DEBUG_SPLITTING
-  _fd_output("split into %d out of %d stores\n", np, n);
+  fd__output("split into %d out of %d stores\n", np, n);
   for (p = 0; p < n; ++p)
-    _fd_output("store %d: ", p), _fd_cprint3(stores[p]);
+    fd__output("store %d: ", p), _fd_cprint3(stores[p]);
 #endif
 
   return parts;
diff --git a/src/util.c b/src/util.c
index a328d31..e204e51 100644
--- a/src/util.c
+++ b/src/util.c
@@ -2,13 +2,15 @@
 #include <stdlib.h>
 #include <stdarg.h>
 
-void _fd_fatal(char *msg)
+#include "util.h"
+
+void fd__fatal(char *msg)
 {
   fprintf(stderr, "%s\n", msg);
   abort(); // XXX
 }
 
-void _fd_error(char *format, ...)
+void fd__error(char *format, ...)
 {
   va_list args;
 
@@ -17,7 +19,7 @@ void _fd_error(char *format, ...)
   va_end (args);
 }
 
-void _fd_output(char *format, ...)
+void fd__output(char *format, ...)
 {
   va_list args;
 
@@ -35,9 +37,9 @@ void fd__info(char *format, ...)
   va_end (args);
 }
 
-#ifndef _fd_debug
+#ifndef fd__debug
 
-void _fd_debug(char *format, ...)
+void fd__debug(char *format, ...)
 {
   va_list args;
 
diff --git a/src/util.h b/src/util.h
new file mode 100644
index 0000000..ee09e98
--- /dev/null
+++ b/src/util.h
@@ -0,0 +1,8 @@
+void fd__fatal(char *msg);
+void fd__error(char *format, ...);
+void fd__output(char *format, ...);
+void fd__info(char *format, ...);
+
+#ifndef fd__debug
+void fd__debug(char *format, ...);
+#endif
diff --git a/src/values-bitmap.c b/src/values-bitmap.c
index c3fcc17..b76d70f 100644
--- a/src/values-bitmap.c
+++ b/src/values-bitmap.c
@@ -58,10 +58,10 @@ fd_value fd_new_value(int min, int max)
   fd_value v;
 
   if (min < MIN_VALUE)
-    _fd_fatal("value less than MIN_VALUE");
+    fd__fatal("value less than MIN_VALUE");
 
   if (max > MAX_VALUE)
-    _fd_fatal("value greater than MAX_VALUE");
+    fd__fatal("value greater than MAX_VALUE");
 
 #ifdef INLINE_DOMAINS
   return (ALL_ONES >> min) & (ALL_ONES << DOMAIN_BITS - max - 1);
@@ -95,10 +95,10 @@ fd_value fd_new_value(int min, int max)
 void _fd_init_domain(DOMAIN_REF_T(domain), int min, int max)
 {
   if (min < MIN_VALUE)
-    _fd_fatal("value less than MIN_VALUE");
+    fd__fatal("value less than MIN_VALUE");
 
   if (max > MAX_VALUE)
-    _fd_fatal("value greater than MAX_VALUE");
+    fd__fatal("value greater than MAX_VALUE");
 
 #if WORDS == 1
   (*domain)[0] = (ALL_ONES >> min) & (ALL_ONES << DOMAIN_BITS - max - 1);
@@ -1278,10 +1278,10 @@ fd_value fd_new_value(int min, int max)
   fd_value v;
 
   if (min < MIN_VALUE)
-    _fd_fatal("value less than MIN_VALUE");
+    fd__fatal("value less than MIN_VALUE");
 
   if (max > MAX_VALUE)
-    _fd_fatal("value greater than MAX_VALUE");
+    fd__fatal("value greater than MAX_VALUE");
 
 #if WORDS == 1
   if (v = malloc(sizeof(*v)))
@@ -1320,10 +1320,10 @@ fd_value fd_new_value(int min, int max)
 void _fd_init_domain(DOMAIN_REF_T(domain), int min, int max)
 {
   if (min < MIN_VALUE)
-    _fd_fatal("value less than MIN_VALUE");
+    fd__fatal("value less than MIN_VALUE");
 
   if (max > MAX_VALUE)
-    _fd_fatal("value greater than MAX_VALUE");
+    fd__fatal("value greater than MAX_VALUE");
 
 #if WORDS == 1
   *domain->map = (ALL_ONES >> min) & (ALL_ONES << DOMAIN_BITS - max - 1);
diff --git a/src/values-intervals.c b/src/values-intervals.c
index a29f557..cdc888a 100644
--- a/src/values-intervals.c
+++ b/src/values-intervals.c
@@ -90,10 +90,10 @@ int _fd_val_max(fd_value domain)
       case FD_MULTIPLE:
 	return v->value.interval.upper;
       case FD_EMPTY:
-	_fd_fatal("empty domain in _fd_val_max");
+	fd__fatal("empty domain in _fd_val_max");
 	/* NOTREACHED */
       default:
-	_fd_fatal("corrupt domain in _fd_val_max?");
+	fd__fatal("corrupt domain in _fd_val_max?");
 	/* NOTREACHED */
     }
 }
@@ -108,10 +108,10 @@ int _fd_val_min(fd_value domain)
       case FD_MULTIPLE:
 	return domain->value.interval.lower;
       case FD_EMPTY:
-	_fd_fatal("empty domain in _fd_val_min");
+	fd__fatal("empty domain in _fd_val_min");
 	/* NOTREACHED */
       default:
-	_fd_fatal("corrupt domain in _fd_val_min?");
+	fd__fatal("corrupt domain in _fd_val_min?");
 	/* NOTREACHED */
     }
 }
@@ -151,10 +151,10 @@ int _fd_val_del_min(fd_value domain)
 
 	break;
       case FD_EMPTY:
-	_fd_fatal("empty domain in _fd_val_del_min");
+	fd__fatal("empty domain in _fd_val_del_min");
 	/* NOTREACHED */
       default:
-	_fd_fatal("corrupt domain in _fd_val_del_min?");
+	fd__fatal("corrupt domain in _fd_val_del_min?");
 	/* NOTREACHED */
     }
 
@@ -198,10 +198,10 @@ int _fd_val_del_max(fd_value domain)
 
 	break;
       case FD_EMPTY:
-	_fd_fatal("empty domain in _fd_val_del_max");
+	fd__fatal("empty domain in _fd_val_del_max");
 	/* NOTREACHED */
       default:
-	_fd_fatal("corrupt domain in _fd_val_del_max?");
+	fd__fatal("corrupt domain in _fd_val_del_max?");
 	/* NOTREACHED */
     }
 
@@ -230,10 +230,10 @@ int _fd_val_contains_val(fd_value domain, int value)
 
 	    break;
 	  case FD_EMPTY:
-	    _fd_fatal("empty domain in _fd_val_contains_val");
+	    fd__fatal("empty domain in _fd_val_contains_val");
 	    /* NOTREACHED */
 	  default:
-	    _fd_fatal("corrupt domain in _fd_val_contains_val?");
+	    fd__fatal("corrupt domain in _fd_val_contains_val?");
 	    /* NOTREACHED */
 	}
 
@@ -249,7 +249,7 @@ void _fd_val_set_value(fd_value domain, int value)
   switch (domain->kind)
     {
       case FD_EMPTY:
-	_fd_fatal("_fd_val_set_val called on a variable with empty domain");
+	fd__fatal("_fd_val_set_val called on a variable with empty domain");
 	/* NOTREACHED */
       case FD_MULTIPLE:
 	domain->kind = FD_SINGLETON;
@@ -262,7 +262,7 @@ void _fd_val_set_value(fd_value domain, int value)
 	  }
 	break;
       default:
-	_fd_fatal("corrupt domain in _fd_val_set_value?");
+	fd__fatal("corrupt domain in _fd_val_set_value?");
 	/* NOTREACHED */
     }
 }
@@ -702,7 +702,7 @@ fd_value _fd_val_clone(fd_value value)
 	    *next = fd_new_value(1, 0);
 	    break;
 	  default:
-	    _fd_fatal("corrupt domain in _fd_val_clone?");
+	    fd__fatal("corrupt domain in _fd_val_clone?");
 	    /* NOTREACHED */
 	}
 
@@ -744,7 +744,7 @@ void _fd_val_copy(fd_value to, fd_value from)
 	    break;
 #endif
 	  default:
-	    _fd_fatal("corrupt domain in _fd_val_copy?");
+	    fd__fatal("corrupt domain in _fd_val_copy?");
 	    /* NOTREACHED */
 	}
 
@@ -769,7 +769,7 @@ int _fd_val_size(fd_value domain)
 	    s += v->value.interval.upper - v->value.interval.lower + 1;
 	    break;
 	  default:
-	    _fd_fatal("corrupt domain in _fd_val_size?");
+	    fd__fatal("corrupt domain in _fd_val_size?");
 	    /* NOTREACHED */
 	}
 
diff --git a/src/values.c b/src/values.c
index 7a2987c..f738a75 100644
--- a/src/values.c
+++ b/src/values.c
@@ -7,6 +7,8 @@
 #include "fdc_int.h"
 #include "values.h"
 
+#include "util.h"
+
 
 #ifdef COMPACT_DOMAINS
 #  include "values-bitmap.c"
diff --git a/src/variables.c b/src/variables.c
index 1f10edc..f72a0b1 100644
--- a/src/variables.c
+++ b/src/variables.c
@@ -9,18 +9,31 @@
 #include "store.h"
 #include "constraints.h"
 
+#include "util.h"
+
 int fd_variables_count = 0;
 __thread fd_int _fd_variables[MAX_VARIABLES];
 
+// variables with singleton domains (ie, constants)
+static fd_int fd__constants[MAX_VALUE + 1];
+
 fd_int *fd__label_vars = 0;	// variables subject to labelling
 int fd__label_vars_count = 0;
 bool *fd__var_labelled;		// identifies the variables subject to labelling
 
-fd_int fd_new(int min, int max)
+
+/*
+  Create and return a new variable with domain { MIN, ..., MAX }.
+  Also store it in the global variable table.
+*/
+static fd_int create_variable(int min, int max)
 {
-  fd_int v = malloc(sizeof(struct fd_int));
+  fd_int v;
 
-  assert(fd_variables_count < MAX_VARIABLES);
+  if (fd_variables_count == MAX_VARIABLES)
+    fd__fatal("too many variables, increase MAX_VARIABLES");
+
+  v = malloc(sizeof(struct fd_int));
 
   if (v)
     {
@@ -39,6 +52,31 @@ fd_int fd_new(int min, int max)
   return v;
 }
 
+/* Return a variable with singleton domain { VALUE }. */
+fd_int fd_const(int value)
+{
+  if (value < MIN_VALUE)
+    fd__fatal("value less than MIN_VALUE");
+
+  if (value > MAX_VALUE)
+    fd__fatal("value greater than MAX_VALUE");
+
+  // only create the variable if it has not yet been created
+  if (fd__constants[value] == NULL)
+    fd__constants[value] = create_variable(value, value);
+
+  return fd__constants[value];
+}
+
+/* Return a variable with domain { MIN, ..., MAX }. */
+fd_int fd_new(int min, int max)
+{
+  if (min == max)
+    return fd_const(min);
+  else
+    return create_variable(min, max);
+}
+
 #ifdef SPLITGO
 /* create a skeletal copy of VARIABLE */ // XXX: description
 fd_int _fd_var_copy(fd_int variable)
@@ -302,7 +340,7 @@ void _fd_var_set_value(fd_int variable, int value)
 void fd_label(fd_int variables[], int n)
 {
   if (fd__label_vars)
-    _fd_fatal("fd_label() can only be called once");
+    fd__fatal("fd_label() can only be called once");
 
   fd__label_vars = malloc(n * sizeof(*fd__label_vars));	// XXX: NULL
   fd__label_vars_count = n;
diff --git a/src/variables.h b/src/variables.h
index 2ca1d01..7e76215 100644
--- a/src/variables.h
+++ b/src/variables.h
@@ -7,6 +7,7 @@ extern fd_int *fd__label_vars;
 extern int fd__label_vars_count;
 extern bool *fd__var_labelled;
 
+fd_int fd_const(int value);
 fd_int fd_new(int min, int max);
 fd_int _fd_var_copy(fd_int variable);
 void _fd_var_copy_domain(fd_int to, fd_int from);
@@ -36,15 +37,15 @@ int _fd_var_intersect(fd_int variable1, fd_int variable2);
 int _fd_var_contains_val(fd_int variable, int value);
 void _fd_var_set_value(fd_int variable, int value);
 
-#define fd_update_domain(op,var,value) \
-  {				       \
-    _fd_var_ ## op(var, value);	       \
-				       \
-    _fd_revise_connected(var);	       \
-  }
+#define fd_update_domain(op, val, var) \
+  do {				       \
+    _fd_var_ ## op(val, var);	       \
+    				       \
+    _fd_revise_connected(this, var);   \
+  } while (0)
 
 #define fd_update_domain_and_check(op, val, var) \
-  {						 \
+  do {						 \
     if (_fd_var_ ## op(val, var))		 \
       {						 \
 	if (fd_domain_empty(var))		 \
@@ -52,7 +53,7 @@ void _fd_var_set_value(fd_int variable, int value);
 						 \
 	_fd_revise_connected(this, var);	 \
       }						 \
-  }
+  } while (0)
 
 // SEARCH
 extern int (*fd__cmp_variables)(fd_int, fd_int);
--
libgit2 0.21.2