1 /*
2  * Alldiff.java
3  * This file is part of JaCoP.
4  * <p>
5  * JaCoP is a Java Constraint Programming solver.
6  * <p>
7  * Copyright (C) 2000-2008 Krzysztof Kuchcinski and Radoslaw Szymanek
8  * <p>
9  * This program is free software: you can redistribute it and/or modify
10  * it under the terms of the GNU Affero General Public License as published by
11  * the Free Software Foundation, either version 3 of the License, or
12  * (at your option) any later version.
13  * <p>
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU Affero General Public License for more details.
18  * <p>
19  * Notwithstanding any other provision of this License, the copyright
20  * owners of this work supplement the terms of this License with terms
21  * prohibiting misrepresentation of the origin of this work and requiring
22  * that modified versions of this work be marked in reasonable ways as
23  * different from the original version. This supplement of the license
24  * terms is in accordance with Section 7 of GNU Affero General Public
25  * License version 3.
26  * <p>
27  * You should have received a copy of the GNU Affero General Public License
28  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
29  */
30 
31 package org.jacop.constraints;
32 
33 import org.jacop.core.IntDomain;
34 import org.jacop.core.IntVar;
35 import org.jacop.core.Store;
36 import org.jacop.core.Var;
37 
38 import java.util.Arrays;
39 import java.util.Comparator;
40 import java.util.LinkedHashSet;
41 import java.util.List;
42 import java.util.concurrent.atomic.AtomicInteger;
43 
44 
45 /**
46  * Alldiff constraint assures that all FDVs has different values. It uses bounds
47  * consistency technique as described in the paper by Alejandro Lopez-Ortiz, Claude-Guy
48  * Quimper, John Tromp, Peter van Beek, "A fast and simple algorithm for bounds
49  * consistency of the alldifferent constraint", Proceedings of the 18th international
50  * joint conference on Artificial intelligence (IJCAI'03), Pages 245-250. Before using
51  * bounds consistency it calls consistency method for ground variables.
52  * <p>
53  * It extends basic functionality of Alldifferent constraint.
54  *
55  * @author Krzysztof Kuchcinski and Radoslaw Szymanek
56  * @version 4.8
57  */
58 
59 public class Alldiff extends Alldifferent {
60 
61     static AtomicInteger idNumber = new AtomicInteger(0);
62 
63     // it stores the store locally so all the private functions which
64     // are part of the consistency function can throw failure exception
65     // without passing store argument every time their function is called.
66     Store store;
67 
68     private Comparator<Element> maxVariable = (o1, o2) -> (o1.var.max() - o2.var.max());
69 
70     private Comparator<Element> minVariable = (o1, o2) -> (o1.var.min() - o2.var.min());
71 
72     private int[] t; // holds the critical capacity pointers; that is, t[i] points to the
73     // predecessor of i in the bounds list.
74     private int[] d; // holds the differences between critical capacities; that is d[i] is
75     // the difference of capacities between bounds[i] and its predecessor
76     // element in the list bounds[t[i]].
77     private int[] h; // holds the Hall interval pointers; that is, if h[i] < i then the
78     // half-open interval [bounds[h[i]],bounds[i]) is contained in a Hall
79     // interval, and otherwise holds a pointer to the Hall interval it
80     // belongs to. This Hall interval is represented by a tree, with the
81     // root containing the value of its right end.
82     private int[] bounds; // is a sorted array of all min’s and max’s.
83 
84     private int nb; // holds the number of unique bounds.
85 
86     private Element[] minsorted, maxsorted;
87 
Alldiff()88     protected Alldiff() {
89     }
90 
91     /**
92      * It constructs the alldiff constraint for the supplied variable.
93      *
94      * @param variables variables which are constrained to take different values.
95      */
Alldiff(IntVar[] variables)96     public Alldiff(IntVar[] variables) {
97 
98         checkInputForNullness("x", variables);
99 
100         this.numberId = idNumber.incrementAndGet();
101         this.list = Arrays.copyOf(variables, variables.length);
102         this.queueIndex = 2;
103 
104         int n = list.length;
105         t = new int[2 * n + 2];
106         d = new int[2 * n + 2];
107         h = new int[2 * n + 2];
108         bounds = new int[2 * n + 2];
109 
110         minsorted = new Element[n];
111         maxsorted = new Element[n];
112         for (int i = 0; i < n; i++) {
113             Element el = new Element();
114             el.var = list[i];
115             minsorted[i] = el;
116             maxsorted[i] = el;
117         }
118 
119         setScope(variables);
120 
121     }
122 
123 
124     /**
125      * It constructs the alldiff constraint for the supplied variable.
126      *
127      * @param variables variables which are constrained to take different values.
128      */
Alldiff(List<? extends IntVar> variables)129     public Alldiff(List<? extends IntVar> variables) {
130         this(variables.toArray(new IntVar[variables.size()]));
131     }
132 
getDefaultConsistencyPruningEvent()133     @Override public int getDefaultConsistencyPruningEvent() {
134         return IntDomain.BOUND;
135     }
136 
impose(Store store)137     @Override public void impose(Store store) {
138 
139         if (list.length == 0)
140             return;
141 
142         super.impose(store);
143         this.store = store;
144 
145     }
146 
consistency(Store store)147     @Override public void consistency(Store store) {
148 
149         if (store.currentQueue == queueIndex) {
150 
151 	    int groundPos = grounded.value();
152             while (!variableQueue.isEmpty()) {
153 
154                 LinkedHashSet<IntVar> fdvs = variableQueue;
155                 variableQueue = new LinkedHashSet<>();
156 
157                 for (IntVar Q : fdvs)
158                     if (Q.singleton()) {
159                         int qPos = positionMapping.get(Q);
160                         if (qPos > groundPos) {
161                             list[qPos] = list[groundPos];
162                             list[groundPos] = Q;
163                             positionMapping.put(Q, groundPos);
164                             positionMapping.put(list[qPos], qPos);
165 			    groundPos++;
166                             for (int i = groundPos; i < list.length; i++)
167                                 list[i].domain.inComplement(store.level, list[i], Q.min());
168                         } else if (qPos == groundPos) {
169 			    groundPos++;
170                             for (int i = groundPos; i < list.length; i++)
171                                 list[i].domain.inComplement(store.level, list[i], Q.min());
172                         }
173                     }
174             }
175 	    grounded.update(groundPos);
176 
177             if (queueIndex + 1 < store.queueNo) {
178                 store.changed[queueIndex + 1].add(this);
179                 return;
180             }
181 
182         }
183 
184         // do {
185         // store.propagationHasOccurred = false;
186 
187         init();
188         updateLB();
189         updateUB();
190 
191         // } while (store.propagationHasOccurred);
192     }
193 
init()194     private void init() {
195         int n = list.length;
196         Arrays.sort(minsorted, 0, n, minVariable);
197         Arrays.sort(maxsorted, 0, n, maxVariable);
198 
199         int min = minsorted[0].var.min();
200         int max = maxsorted[0].var.max() + 1;
201         int last = min - 2;
202         int nb = 0;
203         bounds[0] = last;
204         int i = 0, j = 0;
205         while (true) {
206             if (i < n && min <= max) {
207                 if (min != last)
208                     bounds[++nb] = last = min;
209 
210                 minsorted[i].minrank = nb;
211                 if (++i < n)
212                     min = minsorted[i].var.min();
213 
214             } else {
215                 if (max != last)
216                     bounds[++nb] = last = max;
217 
218                 maxsorted[j].maxrank = nb;
219                 if (++j == n)
220                     break;
221 
222                 max = maxsorted[j].var.max() + 1;
223             }
224         }
225         this.nb = nb;
226         bounds[nb + 1] = bounds[nb] + 2;
227     }
228 
updateLB()229     private void updateLB() {
230 
231         for (int i = 1; i <= nb + 1; i++) {
232             t[i] = h[i] = i - 1;
233             d[i] = bounds[i] - bounds[i - 1];
234         }
235         for (int i = 0; i < this.list.length; i++) {
236             int x = maxsorted[i].minrank;
237             int y = maxsorted[i].maxrank;
238             int z = pathmax(t, x + 1);
239             int j = t[z];
240 
241             if (--d[z] == 0) {
242                 t[z] = z + 1;
243                 z = pathmax(t, t[z]);
244                 t[z] = j;
245             }
246             pathset(t, x + 1, z, z);
247             if (d[z] < bounds[z] - bounds[y]) {
248                 throw store.failException;
249             }
250             if (h[x] > x) {
251                 int w = pathmax(h, h[x]);
252                 maxsorted[i].var.domain.inMin(store.level, maxsorted[i].var, bounds[w]);
253                 pathset(h, x, w, w);
254             }
255             if (d[z] == bounds[z] - bounds[y]) {
256                 pathset(h, h[y], j - 1, y);
257                 h[y] = j - 1;
258             }
259         }
260     }
261 
updateUB()262     private void updateUB() {
263 
264         for (int i = 0; i <= nb; i++) {
265             t[i] = h[i] = i + 1;
266             d[i] = bounds[i + 1] - bounds[i];
267         }
268         for (int i = this.list.length - 1; i >= 0; i--) {
269             int x = minsorted[i].maxrank;
270             int y = minsorted[i].minrank;
271             int z = pathmin(t, x - 1);
272             int j = t[z];
273             if (--d[z] == 0) {
274                 t[z] = z - 1;
275                 z = pathmin(t, t[z]);
276                 t[z] = j;
277             }
278             pathset(t, x - 1, z, z);
279             if (d[z] < bounds[y] - bounds[z]) {
280                 throw store.failException;
281             }
282             if (h[x] < x) {
283                 int w = pathmin(h, h[x]);
284                 minsorted[i].var.domain.inMax(store.level, minsorted[i].var, bounds[w] - 1);
285                 pathset(h, x, w, w);
286             }
287             if (d[z] == bounds[y] - bounds[z]) {
288                 pathset(h, h[y], j + 1, y);
289                 h[y] = j + 1;
290             }
291         }
292     }
293 
pathset(int[] v, int start, int end, int to)294     private void pathset(int[] v, int start, int end, int to) {
295         int next = start;
296         int prev = next;
297         while (prev != end) {
298             next = v[prev];
299             v[prev] = to;
300             prev = next;
301         }
302     }
303 
pathmin(int[] v, int i)304     private int pathmin(int[] v, int i) {
305         while (v[i] < i)
306             i = v[i];
307 
308         return i;
309     }
310 
pathmax(int[] v, int i)311     private int pathmax(int[] v, int i) {
312         while (v[i] > i)
313             i = v[i];
314 
315         return i;
316     }
317 
toString()318     @Override public String toString() {
319 
320         StringBuilder result = new StringBuilder(id());
321         result.append(" : Alldiff([");
322 
323         for (int i = 0; i < list.length; i++) {
324             result.append(list[i]);
325             if (i < list.length - 1)
326                 result.append(", ");
327         }
328 
329         result.append("])");
330 
331         return result.toString();
332 
333     }
334 
335     // Overwritten as QueueForwardQueue checks that constraint has declared this method.
queueVariable(int level, Var var)336     @Override public void queueVariable(int level, Var var) {
337         super.queueVariable(level, var);
338     }
339 
340     private static class Element {
341         private IntVar var;
342         private int minrank, maxrank;
343     }
344 
345 }
346 
347