1 /*
2  * GCC.java
3  * This file is part of JaCoP.
4  * <p>
5  * JaCoP is a Java Constraint Programming solver.
6  * <p>
7  * Copyright (C) 2008 Jocelyne Lotfi 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.api.SatisfiedPresent;
34 import org.jacop.api.Stateful;
35 import org.jacop.api.UsesQueueVariable;
36 import org.jacop.core.*;
37 
38 import java.util.*;
39 import java.util.concurrent.atomic.AtomicInteger;
40 import java.util.stream.Stream;
41 
42 /**
43  * GCC constraint counts the number of occurences of given
44  * values in x variables. The counters are specified by y's.
45  * The occurence of all values in the domain of xs is counted.
46  * <p>
47  * We would like to thank Irit Katriel for making the code of GCC in C she wrote
48  * available to us.
49  *
50  * @author Jocelyne Lotfi and Radoslaw Szymanek.
51  * @version 4.8
52  */
53 
54 public class GCC extends Constraint implements UsesQueueVariable, Stateful, SatisfiedPresent {
55 
56     /**
57      * TODO An improvement to increase the incrementality even further.
58      * <p>
59      * 1. The first matching uses minimal values. Remember which minimal value has changed which
60      * removed from the domain the value which was used in the matching.
61      * Reuse from old matching 1 all values smaller than the minimal which has changed.
62      * <p>
63      * Similar principle applies to matching 2 (skip the positions (variables) until
64      * the first index for which m1 did change or for which the m2 value is no longer in the
65      * domain.
66      * <p>
67      * <p>
68      * 2. Use IndexDomainView instead of local solution.
69      * <p>
70      * <p>
71      * 3. boolean variable first - is it only once in the consistency function? Then this functionality
72      * can be moved out of the while(newPropagation), if it should be executed every time consistency
73      * is executed then (it should be setup to true somewhere).
74      */
75 
76     boolean firstConsistencyCheck = true;
77 
78     static AtomicInteger idNumber = new AtomicInteger(0);
79 
80     /**
81      * The array which stores the first computed matching, which may not take into account
82      * the lower bound of count variables.
83      */
84     private int[] match1;
85 
86     /**
87      * The array which stores the second computed matching, which may not take into account
88      * the upper bound of count variables.
89      */
90     private int[] match2;
91 
92     /**
93      * The array which stores the third proper matching, constructed from the first one
94      * and second one so both lower and upper bounds are respected.
95      */
96     private int[] match3;
97 
98     private int[] match1XOrder;
99     private int[] match2XOrder;
100     private int[] nbOfMatchPerY;
101 
102     private int[] compOfY;
103 
104     private XDomain[] xDomain;
105     private int[][] yDomain;
106 
107     private int xSize;
108     private int ySize;
109 
110     private ArrayDeque<Integer> S1;
111     private ArrayDeque<Component> S2;
112     private PriorityQueue<XDomain> pFirst, pSecond;
113     private PriorityQueue<Integer> pCount;
114 
115     private final static boolean debug = false;
116 
117     private int[] domainHash;
118 
119     private Map<IntVar, Integer> xNodesHash;
120     private Set<IntVar> xVariableToChange;
121 
122     TimeStamp<Integer> stamp;
123 
124     private int stampValue;
125     int firstConsistencyLevel;
126 
127     /**
128      * It specifies variables x whose values are counted.
129      */
130     public IntVar[] x;
131 
132     /**
133      * It species variables counters for counting occurences of each possible value from the
134      * intial domain of x variables.
135      */
136     protected IntVar[] counters;
137 
138     private Comparator<XDomain> compareLowerBound = (o1, o2) -> {
139         if (o1.min() < o2.min())
140             return -1;
141         else if (o1.min() > o2.min())
142             return 1;
143         return 0;
144     };
145 
146     private Comparator<XDomain> sortPriorityMinOrder = (o1, o2) -> {
147         if (o1.max() < o2.max())
148             return -1;
149         else if (o1.max() > o2.max())
150             return 1;
151 
152         return 0;
153     };
154 
155     private Comparator<Integer> sortPriorityMaxOrder = (e1, e2) -> -e1.compareTo(e2);
156 
157     /**
158      * It constructs global cardinality constraint.
159      *
160      * @param x        variables which values are counted.
161      * @param counters variables which count the values.
162      */
GCC(IntVar[] x, IntVar[] counters)163     public GCC(IntVar[] x, IntVar[] counters) {
164 
165         checkInputForNullness(new String[] {"x", "counters"}, x, counters);
166         checkInputForDuplicationSkipSingletons("x", x);
167 
168         this.queueIndex = 1;
169         numberId = idNumber.incrementAndGet();
170 
171         counters = removeZeroCounters(x, counters);
172 
173         xSize = x.length;
174         ySize = counters.length;
175 
176         this.x = new IntVar[xSize];
177         this.counters = new IntVar[ySize];
178 
179         System.arraycopy(x, 0, this.x, 0, xSize);
180         System.arraycopy(counters, 0, this.counters, 0, ySize);
181 
182         this.xDomain = new XDomain[xSize];
183         this.yDomain = new int[2][ySize];
184 
185         // rest of the init
186         match1 = new int[xSize];
187         match2 = new int[xSize];
188         match3 = new int[xSize];
189         match1XOrder = new int[xSize];
190         match2XOrder = new int[xSize];
191 
192         nbOfMatchPerY = new int[ySize];
193         compOfY = new int[ySize];
194 
195         S1 = new ArrayDeque<>();
196         S2 = new ArrayDeque<>();
197         pFirst = new PriorityQueue<>(10, sortPriorityMinOrder);
198         pSecond = new PriorityQueue<>(10, sortPriorityMinOrder);
199         pCount = new PriorityQueue<>(10, sortPriorityMaxOrder);
200 
201         xNodesHash = Var.createEmptyPositioning();
202         xVariableToChange = new HashSet<>();
203 
204         setScope(Stream.concat(Arrays.stream(x), Arrays.stream(counters)));
205 
206     }
207 
208     private Set<IntVar> zeroCounters;
209 
210     /**
211      * Fix suggested by Radek: a set that keeps track of the variables that have changed and need to be revisited in the consistency method
212      */
213     private Set<Var> changedVariables = new HashSet<>();
214 
removeZeroCounters(IntVar[] x, IntVar[] counters)215     private IntVar[] removeZeroCounters(IntVar[] x, IntVar[] counters) {
216 
217         IntVar[] result;
218 
219         // here I will put normalization
220         IntervalDomain d = new IntervalDomain();
221 
222         for (IntVar aX : x)
223             d = (IntervalDomain) d.union(aX.domain);
224 
225         // I check the consistency of the x and y variable
226         if (d.getSize() != counters.length && (d.max() - d.min() + 1) != counters.length)
227             // if there are more y variable than x variable there is a mistake of conseption
228             // as the rest of y variables are 0 in any case. The problem is to know which y variable
229             // should not be here. With normalization we assume that it is the last ones in the
230             // list but it is an assumption, it's better to throw an exception there and let the
231             // user determine what is correct.
232             throw new IllegalArgumentException("GCC failure : join domain of x variables doesn't cover all count variables");
233 
234         // no changes required
235         if (d.getSize() == counters.length)
236             return counters;
237 
238         // zero counters encountered.
239         result = new IntVar[d.getSize()];
240         zeroCounters = new HashSet<>();
241 
242         int i = 0;
243         for (int k = d.min(); k <= d.max(); k++)
244             if (d.contains(k))
245                 result[i++] = counters[k - d.min()];
246             else
247                 zeroCounters.add(counters[k - d.min()]);
248 
249         return result;
250     }
251 
252     /**
253      * It constructs global cardinality constraint.
254      *
255      * @param x        variables which values are counted.
256      * @param counters variables which count the values.
257      */
GCC(List<? extends IntVar> x, List<? extends IntVar> counters)258     public GCC(List<? extends IntVar> x, List<? extends IntVar> counters) {
259 
260         this(x.toArray(new IntVar[x.size()]), counters.toArray(new IntVar[counters.size()]));
261 
262     }
263 
removeLevel(int level)264     @Override public void removeLevel(int level) {
265         if (level == firstConsistencyLevel)
266             firstConsistencyCheck = true;
267     }
268 
consistency(Store store)269     @Override public void consistency(Store store) {
270 
271         // the stamp is here to represent the number of x variable still
272         // not singleton and that need to be pruned (the rest is set and
273         // doesn't need to be pass though the whole calculation of matching
274         // and SCC's). The same trick can not be apply to the y as the order
275         // matter a lot.
276 
277         // I take out all the xNodes that are singleton and I put them
278         // after the stamp value
279         if (firstConsistencyCheck) {
280 
281             if (zeroCounters != null)
282                 for (IntVar zeroCounter : zeroCounters)
283                     zeroCounter.domain.in(store.level, zeroCounter, 0, 0);
284 
285             int k = 0;
286 
287             stamp.update(xSize);
288 
289             while (k < stamp.value()) {
290                 if (x[k].singleton()) {
291                     if (stamp.value() > 0) {
292                         stamp.update(stamp.value() - 1);
293                         putToTheEnd(x, k);
294                     }
295                 } else {
296                     // no incrementation if there is a modification in the
297                     // xNodes table. The variable in the i position is no more
298                     // the same and need to be check also.
299                     k++;
300                 }
301             }
302             firstConsistencyCheck = false;
303             firstConsistencyLevel = store.level;
304 
305             assert checkXorder() : "Inconsistent X variable order: " + Arrays.toString(this.x);
306 
307         }
308 
309         // no need to rerun the consistancy function as the reduction on x domains doesn't affect
310         // the matching and the y count is base on the matching and doesn't affect it. So
311         // rerunning the constraint doesn't bring anything new. We can suppose that y counting
312         // achieve bound consistancy.
313 
314 
315         do {
316 
317             store.propagationHasOccurred = false;
318 
319             // Fix suggested by Radek (moved from queueVariable)
320             Set<Var> changedVariablesCopy = this.changedVariables;
321             this.changedVariables = new HashSet<Var>();
322             for (Var var : changedVariablesCopy) {
323                 // if v is singleton and is an X variable
324                 if (var.singleton() && xNodesHash.containsKey(var)) {
325                     // if
326                     if (xNodesHash.get(var) < stamp.value()) { // changing '<=' to '<' (KK)
327                         if (debug)
328                             System.out.println(" in xVariableToChange: " + var);
329                         if (stamp.value() > 0) {
330                             stamp.update(stamp.value() - 1);
331                             putToTheEnd(x, xNodesHash.get(var));
332                         }
333                     }
334                 }
335             }
336 
337             assert checkXorder() : "Inconsistent X variable order: " + Arrays.toString(this.x);
338 
339             if (debug) {
340                 System.out.println("XNodes");
341                 for (int i = 0; i < xSize; i++)
342                     System.out.println(x[i]);
343                 System.out.println("stamp before " + stamp.value());
344             }
345 
346             stampValue = stamp.value();
347 
348             if (debug) {
349                 System.out.println("stamp after " + stampValue);
350                 System.out.println("XDomain");
351             }
352             // put in the xDomain all xNodes that are not singleton
353 
354             for (int i = 0; i < stampValue; i++) {
355                 xDomain[i].setDomain(findPosition(x[i].min(), domainHash), findPosition(x[i].max(), domainHash));
356 
357                 xDomain[i].twin = x[i];
358                 if (debug)
359                     System.out.println(xDomain[i]);
360             }
361             if (debug)
362                 System.out.println("YDomain");
363 
364             // put all yNodes in yDomain
365             for (int i = 0; i < ySize; i++) {
366                 yDomain[0][i] = counters[i].min();
367                 yDomain[1][i] = counters[i].max();
368 
369                 if (debug)
370                     System.out.println(yDomain[i]);
371             }
372 
373             if (debug)
374                 System.out.println("take out singleton xNodes");
375 
376             // check all xNodes and if singleton change the yDomain value
377             // to count down the xNode already link to this yNode
378             for (int i = 0; i < xSize; i++) {
379                 if (x[i].singleton()) {
380                     //Change, check.
381                     int value = findPosition(x[i].value(), domainHash);
382                     if (yDomain[0][value] > 0) {
383                         yDomain[0][value]--;
384                     }
385                     yDomain[1][value]--;
386                     if (yDomain[1][value] < 0)
387                         throw Store.failException;
388                 }
389             }
390 
391             if (debug) {
392                 System.out.println("pass in consistency");
393                 System.out.println("YDomain");
394                 for (int i = 0; i < ySize; i++)
395                     System.out.println(yDomain[i]);
396             }
397 
398             sortXByDomainMin();
399 
400             FindGeneralizedMatching();
401             SCCs();
402             // I do the countConcistancy before the x pruning so the
403             // change in x variable doesn't affect the pruning of y variable
404             countBoundConsistency(store);
405 
406             // do the pruning
407             for (int j = 0; j < stampValue; j++) {
408 
409                 assert ((match3[j] >= 0) && (match3[j] < ySize));
410                 assert ((compOfY[match3[j]] >= 0) && (compOfY[match3[j]] <= ySize));
411 
412                 int cutMin = xDomain[j].min();
413                 int cutMax = xDomain[j].max();
414 
415                 if (debug)
416                     System.out.println("cutmax " + cutMax);
417 
418                 while (compOfY[match3[j]] != compOfY[cutMin])
419                     cutMin++;
420 
421                 while (compOfY[match3[j]] != compOfY[cutMax]) {
422                     cutMax--;
423                 }
424 
425                 int id = xNodesHash.get(xDomain[j].twin);
426 
427                 if (debug)
428                     System.out.println("do pruning [" + x[id].min() + "," + x[id].max() + "] => [" + cutMin + "," + cutMax + "]");
429 
430                 xDomain[j].setDomain(cutMin, cutMax);
431                 IntVar v = x[id];
432                 v.domain.in(store.level, v, domainHash[cutMin], domainHash[cutMax]);
433 
434             }
435 
436             // check if the solution is still valid now the prunning done
437             // useful if max bound reduction lead to a non-existing number and
438             // the prunning is so report to the next upper bound (not necessary fitting)
439             for (int i = 0; i < xSize; i++) {
440                 if (x[i].singleton()) {
441                     // Change, check.
442                     int value = findPosition(x[i].value(), domainHash);
443                     yDomain[1][value]--;
444                     if (yDomain[1][value] < 0) {
445                         if (debug)
446                             System.out.println("failure in putting back yNodes domain");
447                         throw Store.failException;
448                     }
449                 }
450             }
451 
452 
453         } while (store.propagationHasOccurred);
454 
455     }
456 
457     /**
458      * A method to be called in asserts that checks whether all grounded X variables are correctly put at the end of the list
459      *
460      * @return false if the X variable order is inconsistent
461      */
checkXorder()462     private boolean checkXorder() {
463 
464         for (int i = this.stamp.value() - 1; i >= 0; i--)
465             if (this.x[i].singleton())
466                 return false;
467 
468         for (int i = this.stamp.value(); i < this.x.length; i++)
469             if (!this.x[i].singleton())
470                 return false;
471 
472         return true;
473     }
474 
getDefaultConsistencyPruningEvent()475     @Override public int getDefaultConsistencyPruningEvent() {
476         return IntDomain.BOUND;
477     }
478 
impose(Store store)479     @Override public void impose(Store store) {
480 
481         stamp = new TimeStamp<>(store, xSize);
482 
483         // first I will put all the xNodes in a hashTable to be able to use
484         // it with the queueVariable function
485         // KK, 2015-10-18
486         // only non ground variables need to be added
487         // no duplicates allowed
488         Var.addPositionMapping(xNodesHash, x, true, this.getClass());
489 
490         // here I will put normalization
491         IntervalDomain d = new IntervalDomain();
492 
493         for (int i = 0; i < xSize; i++)
494             d = (IntervalDomain) d.union(x[i].domain);
495 
496         // I check the consistency of the x and y variable
497         if (d.getSize() != ySize)
498             // if there are more y variable than x variable there is a mistake of conseption
499             // as the rest of y variables are 0 in any case. The problem is to know which y variable
500             // should not be here. With normalization we assume that it is the last ones in the
501             // list but it is an assumption, it's better to throw an exception there and let the
502             // user determine what is correct.
503             throw new IllegalArgumentException("GCC failure : join domain of x variables doesn't cover all count variables");
504 
505         domainHash = new int[d.getSize()];
506         IntervalDomainValueEnumeration venum = new IntervalDomainValueEnumeration(d);
507         int i = 0;
508         do {
509 
510             Integer j = venum.nextElement();
511             domainHash[i++] = j;
512 
513         } while (venum.hasMoreElements());
514 
515         for (i = 0; i < xSize; i++)
516             this.xDomain[i] = new XDomain(x[i], findPosition(x[i].min(), domainHash), findPosition(x[i].max(), domainHash));
517 
518         for (i = 0; i < ySize; i++) {
519             this.yDomain[0][i] = counters[i].min();
520             this.yDomain[1][i] = counters[i].max();
521         }
522         super.impose(store);
523 
524     }
525 
queueVariable(int level, Var var)526     @Override public void queueVariable(int level, Var var) {
527         if (debug)
528             System.out.println("in queue variable " + var + " level " + level);
529 
530         // Fix suggested by Radek: the queueVariable function should store the variables that are changing in a HashSet
531         this.changedVariables.add(var);
532     }
533 
satisfied()534     @Override public boolean satisfied() {
535 
536         if (!grounded())
537             return false;
538 
539         int count[] = new int[domainHash.length];
540 
541         for (IntVar xVar : x) {
542             int xValue = xVar.value();
543             int position = 0;
544             for (; position < count.length && domainHash[position] != xValue; position++)
545                 ;
546             assert (position < count.length);
547             count[position]++;
548         }
549 
550         for (int i = 0; i < counters.length; i++)
551             if (counters[i].value() != count[i])
552                 return false;
553 
554         return true;
555     }
556 
557     @Override public String toString() {
558 
559         StringBuilder toString = new StringBuilder(id());
560 
561         toString.append(" : GCC ([");
562         for (int i = 0; i < xSize - 1; i++)
563             toString.append(x[i].toString()).append(", ");
564         toString.append(x[xSize - 1].toString());
565         toString.append("], [");
566         for (int j = 0; j < ySize - 1; j++)
567             toString.append(counters[j].toString()).append(", ");
568         toString.append(counters[ySize - 1].toString()).append("])");
569 
570         return toString.toString();
571 
572     }
573 
574 
575     // private methods
576 
577     //-----------------------FIND_GENERALIZED_MATCHING--------------------------------//
578 
579     private void FindGeneralizedMatching() {
580 
581         Arrays.fill(nbOfMatchPerY, 0);
582 
583         if (debug) {
584             System.out.println("XDomain");
585             for (int i = 0; i < stampValue; i++)
586                 System.out.println(i + " [" + xDomain[i].min() + "-" + xDomain[i].max() + "]");
587         }
588         // first pass
589         firstPass();
590 
591         // check we are in the good ranges for match1 and match1XOrder
592         assert (checkFirstPass());
593 
594         secondPass();
595 
596         assert (checkSecondPass());
597 
598         thirdPass();
599 
600         assert (checkThirdPass());
601 
602     }
603 
604     private boolean checkFirstPass() {
605 
606         for (int j = 0; j < stampValue; j++) {
607             assert (match1[j] >= 0);
608             assert (match1[j] < ySize);
609             assert (match1XOrder[j] >= 0);
610             assert (match1XOrder[j] < stampValue);
611         }
612 
613         return true;
614     }
615 
616     private boolean checkSecondPass() {
617 
618         for (int j = 1; j < stampValue; j++)
619             assert (match2[match2XOrder[j]] >= match2[match2XOrder[j - 1]]);
620 
621         return true;
622     }
623 
624     private boolean checkThirdPass() {
625 
626         for (int j = 0; j < stampValue; j++) {
627             assert (xDomain[j].min() <= match3[j]);
628             assert (xDomain[j].max() >= match3[j]);
629         }
630 
631         return true;
632     }
633 
634     private void firstPass() {
635 
636         pFirst.clear();
637         //		int j = 0;
638         int xIndex = 0;
639         int maxY;
640         int match1XOrderIndex = 0;
641         int top;
642 
643         for (int i = 0; i < ySize; i++) {
644             // first we add all the x which min domain is y
645             while ((xIndex < stampValue) && xDomain[xIndex].min() == i) {
646                 // add a new element with index to get it back after the good element
647                 // and the max of the domain of xNode to sort them.
648                 xDomain[xIndex].index = xIndex;
649                 pFirst.add(xDomain[xIndex]);
650                 xIndex++;
651             }
652             int u = 0;
653             // second we add the maximum of these xs possible to the current y
654             maxY = yDomain[1][i];
655             while (!pFirst.isEmpty() && u < maxY) {
656                 top = (pFirst.remove()).index; // index of the first element of pFirst
657                 match1[top] = i;
658                 u++;
659                 // match1XOrder gives the order in which xs where in the priority queue
660                 // that sorted them by domain max by group of domain min.
661                 // That is there are first sorted by domain min and after by domain max.
662                 match1XOrder[match1XOrderIndex] = top;
663                 match1XOrderIndex++;
664 
665                 if (xDomain[top].max() < i) {
666                     if (debug)
667                         System.out.println("failure first pass");
668 
669                     throw Store.failException;
670                     // it was checked that the min == i so max cannot be under min. Well, yes there are cases
671                     // where it's useful.
672                 }
673                 //				j++;
674             }
675         }
676 
677         // do we have to check that the queue is empty ? If not it means that an x was not paired.
678         // I add the test on the queue. It is possible if the maxY = 0 and is the last one visited
679         // otherwise the element in the queue can be used by the next yNode.
680         if (!pFirst.isEmpty()) {
681 
682             if (debug)
683                 System.out.println("failure the queue is not empty");
684 
685             throw Store.failException;
686 
687         }
688 
689         if (debug) {
690 
691             System.out.print("match1Xorder : ");
692             for (int aMatch1XOrder : match1XOrder)
693                 System.out.print(aMatch1XOrder + " ");
694 
695             System.out.println("");
696             System.out.print("match1 : ");
697 
698             for (int aMatch1 : match1)
699                 System.out.print(aMatch1 + " ");
700 
701             System.out.println("");
702         }
703 
704     }
705 
706     private void secondPass() {
707 
708         pSecond.clear();
709         //		int j = 0;
710         int top;
711         int xIndex = 0;
712         int minY;
713         int match2XOrderIndex = 0;
714         int order;
715 
716         for (int i = 0; i < ySize; i++) {
717             // I should iterate on the match1XOrder instead of the normal order
718             // we take all the xs that where matched with yi in the first pass.
719             while ((xIndex < stampValue) && (match1[match1XOrder[xIndex]] == i)) {
720                 order = match1XOrder[xIndex];
721                 xDomain[order].index = order;
722                 pSecond.add(xDomain[order]);
723                 xIndex++;
724             }
725             minY = yDomain[0][i];
726             for (int l = 0; l < minY; l++) {
727                 if (pSecond.isEmpty()) {
728                     // failure need to be expressed
729                     if (debug)
730                         System.out.println("failure second pass");
731 
732                     throw Store.failException;
733                 }
734                 top = pSecond.remove().index;
735                 //change, check.
736                 match2[top] = i;
737 
738                 match2XOrder[match2XOrderIndex] = top;
739                 match2XOrderIndex++;
740                 nbOfMatchPerY[i]++;
741                 //				j++;
742             }
743             while (!pSecond.isEmpty() && ((pSecond.element().max()) < i + 1)) {
744                 top = pSecond.remove().index;
745                 // change, check.
746                 match2[top] = i;
747                 match2XOrder[match2XOrderIndex] = top;
748                 match2XOrderIndex++;
749                 nbOfMatchPerY[i]++;
750                 //				j++;
751             }
752         }
753 
754 
755         if (debug) {
756 
757             System.out.print("match2Xorder : ");
758 
759             for (int aMatch2XOrder : match2XOrder)
760                 System.out.print(aMatch2XOrder + " ");
761 
762             System.out.println("");
763 
764             System.out.print("match2 : ");
765             for (int aMatch2 : match2)
766                 System.out.print(aMatch2 + " ");
767 
768             System.out.println("");
769         }
770 
771     }
772 
773     private void thirdPass() {
774 
775         int xIndex = stampValue - 1;
776         int e;
777         int x = 0;
778 
779         System.arraycopy(match2, 0, match3, 0, stampValue);
780 
781         for (int i = ySize - 1; i >= 0; i--) {
782             while ((xIndex >= 0) && (match2[match2XOrder[xIndex]] > i))
783                 xIndex--;
784 
785             e = nbOfMatchPerY[i] - yDomain[1][i]; // excess of y mates
786             while (e > 0) {
787 
788                 assert (match2[match2XOrder[xIndex]] == i);
789 
790                 while (xIndex >= 0) {
791                     x = match2XOrder[xIndex];
792                     if (match1[x] == i)
793                         xIndex--;
794                     else
795                         break;
796                 }
797 
798                 assert (match1[x] < i);
799                 assert (match2[x] == i);
800 
801                 match3[x] = match1[x];
802                 nbOfMatchPerY[i]--;
803                 nbOfMatchPerY[match1[x]]++;
804                 xIndex--;
805                 e--;
806             }
807         }
808         if (debug) {
809             System.out.print("match3 : ");
810             for (int aMatch3 : match3)
811                 System.out.print(aMatch3 + " ");
812             System.out.println("");
813         }
814     }
815 
816     //--------------------------------SCCs-------------------------------------//
817 
818     private void SCCs() {
819 
820         int sccNb, C;
821         int maxYReachedFromS, maxYReachesS;
822         int minYReachedFromS, minYReachesS;
823         int[] compReachesLeft = new int[ySize];
824         int[] compReachesRight = new int[ySize];
825         int[] yReachesLeft = new int[ySize];
826         int[] yReachesRight = new int[ySize];
827 
828         for (int i = 0; i < ySize; i++)
829             compOfY[i] = i;
830 
831         sccNb = SCCsWithoutS(compReachesLeft, compReachesRight, yReachesLeft, yReachesRight);
832         // now compReaches(Left, Right) and compOfY contain the left and right most y per comp and to which comp a y belong
833         if (debug) {
834             System.out.println("sccNb : " + sccNb);
835             System.out.println("compReachesLeft ");
836             for (int aCompReachesLeft : compReachesLeft)
837                 System.out.print(aCompReachesLeft + " ");
838 
839             System.out.println("");
840             System.out.println("compReachesRight ");
841             for (int aCompReachesRight : compReachesRight)
842                 System.out.print(aCompReachesRight + " ");
843 
844             System.out.println("");
845             System.out.println("compOfY ");
846             for (int aCompOfY : compOfY)
847                 System.out.print(aCompOfY + " ");
848 
849             System.out.println("");
850         }
851         boolean[] reachedFromS = new boolean[sccNb];
852         boolean[] reachesS = new boolean[sccNb];
853 
854         // reachedFromS and reachesS need to contain only value false.
855         // by default satisfied by Java, therefore the two lines below are not needed.
856         // Arrays.fill(reachedFromS, false);
857         // Arrays.fill(reachesS, false);
858 
859         // init reachedFromS and reachesS
860         int comp;
861         for (int i = 0; i < ySize; i++) {
862             comp = compOfY[i];
863             assert (comp >= 0);
864             assert (comp <= sccNb);
865 
866             // if there are stictly more match to y than the minimum required
867             // an edge exist from s to y
868             if (yDomain[0][i] < nbOfMatchPerY[i])
869                 reachedFromS[comp] = true;
870 
871             // if there are strictly less match to y than the maximum possible
872             // an edge exist from y to s
873             if (yDomain[1][i] > nbOfMatchPerY[i])
874                 reachesS[comp] = true;
875         }
876 
877         maxYReachedFromS = -1;
878         maxYReachesS = -1;
879 
880         for (int i = 0; i < ySize; i++) {
881             C = compOfY[i];
882 
883             assert (C >= 0);
884             assert (C <= sccNb);
885 
886             // if the max y that can be reached is greater than the current y,
887             // that the comp it belongs to can be reached from s.
888             if (maxYReachedFromS >= i)
889                 reachedFromS[C] = true;
890 
891             // if it's reached we can extand the max y reached to the compReachesRight of the component
892             if (reachedFromS[C])
893                 maxYReachedFromS = Math.max(maxYReachedFromS, compReachesRight[C]);
894 
895             // same in the other way : if the ReachesLeft of the comp is under the max y that
896             // reaches S this comp reaches S
897             if (compReachesLeft[C] <= maxYReachesS)
898                 reachesS[C] = true;
899 
900             // if it's reachesS we can extand the max Y that reaches it to the current y.
901             if (reachesS[C])
902                 maxYReachesS = Math.max(maxYReachesS, i);
903 
904         }
905 
906         // same as before but for minimum
907 
908         minYReachedFromS = ySize;
909         minYReachesS = ySize;
910 
911         for (int i = ySize - 1; i >= 0; i--) {
912             C = compOfY[i];
913             assert (C >= 0);
914             assert (C <= sccNb);
915             if (minYReachedFromS <= i)
916                 reachedFromS[C] = true;
917 
918             if (reachedFromS[C])
919                 minYReachedFromS = Math.min(minYReachedFromS, compReachesLeft[C]);
920 
921             if (compReachesRight[C] >= minYReachesS)
922                 reachesS[C] = true;
923 
924             if (reachesS[C])
925                 minYReachesS = Math.min(minYReachesS, i);
926 
927         }
928 
929         // merge all comp that are strongly connected through s and
930         // give this new comp a new number
931 
932         for (int i = 0; i < ySize; i++)
933 
934             if (reachesS[compOfY[i]] && reachedFromS[compOfY[i]])
935                 compOfY[i] = sccNb;
936 
937         if (debug) {
938             System.out.println("compOfY after S ");
939             for (int aCompOfY : compOfY)
940                 System.out.print(aCompOfY + " ");
941 
942             System.out.println("");
943         }
944     }
945 
946     private int SCCsWithoutS(int[] compReachesLeft, int[] compReachesRight, int[] yReachesLeft, int[] yReachesRight) {
947 
948         Component C, C1;
949         int sccNb = 0;
950         S1.clear();
951         S2.clear();
952 
953         ReachedFromY(yReachesLeft, yReachesRight);
954 
955         // init all componant as containing only one y and set these component
956         // reachesLeft and reachesRight to y reachesLeft and Right
957         for (int y = 0; y < ySize; y++) {
958             compReachesLeft[y] = yReachesLeft[y];
959             compReachesRight[y] = yReachesRight[y];
960         }
961 
962         for (int y = 0; y < ySize; y++) {
963             // set comp to (root, rightmostY, maxX)
964             C = new Component(y, y, yReachesRight[y]);
965 
966             if (S2.isEmpty()) {
967                 S1.push(y);
968                 S2.push(C);
969                 continue;
970             }
971 
972             // once S2 not empty
973             // this first part treat the case we have a new component.
974             // (c1.max < C.root)
975 
976             while ((!S2.isEmpty()) && ((S2.peek().maxX < C.root))) {
977                 compReachesLeft[sccNb] = ySize;
978                 compReachesRight[sccNb] = -1;
979 
980                 assert (!S1.isEmpty());
981 
982                 C1 = S2.pop();
983                 while (!S1.isEmpty() && S1.peek() >= C1.root && S1.peek() <= C1.rightmostY) {
984                     int popY;
985                     assert (!S1.isEmpty());
986                     popY = S1.pop();
987                     compOfY[popY] = sccNb;
988                     compReachesLeft[sccNb] = Math.min(compReachesLeft[sccNb], yReachesLeft[popY]);
989                     compReachesRight[sccNb] = Math.max(compReachesRight[sccNb], yReachesRight[popY]);
990                 }
991                 sccNb++;
992             }
993 
994             assert (S2.isEmpty() || S2.peek().maxX >= C.root);
995 
996             // this second part treat the case the new c1 is in fact attainable by the current component
997 
998             while (!S2.isEmpty() && yReachesLeft[y] <= S2.peek().rightmostY) {
999                 assert (!S2.isEmpty());
1000                 C1 = S2.pop();
1001                 C.maxX = Math.max(C.maxX, C1.maxX);
1002                 C.root = C1.root; // as they are taken in order from left to right c1.root is always < C.root
1003                 C.rightmostY = y; // same remark
1004             }
1005 
1006             assert (S2.isEmpty() || ((yReachesLeft[y] > S2.peek().rightmostY) && (S2.peek().maxX >= C.root)));
1007 
1008             S1.push(y);
1009             S2.push(C);
1010         } // end for
1011 
1012 
1013         // for every component still on the pile update compOfY, compReachesLeft and Right, and sccNb
1014         while (!S2.isEmpty()) {
1015             assert (!S1.isEmpty());
1016             C = S2.pop();
1017             compReachesLeft[sccNb] = ySize;
1018             compReachesRight[sccNb] = -1;
1019 
1020             while (!S1.isEmpty() && S1.peek() >= C.root && S1.peek() <= C.rightmostY) {
1021                 int y;
1022                 assert (!S1.isEmpty());
1023                 y = S1.pop();
1024                 compOfY[y] = sccNb;
1025                 compReachesLeft[sccNb] = Math.min(compReachesLeft[sccNb], yReachesLeft[y]);
1026                 compReachesRight[sccNb] = Math.max(compReachesRight[sccNb], yReachesRight[y]);
1027             }
1028             sccNb++;
1029         }
1030 
1031         assert (S1.isEmpty() && S2.isEmpty());
1032         return sccNb;
1033     }
1034 
1035     private void ReachedFromY(int[] yReachesLeft, int[] yReachesRight) {
1036 
1037 
1038         for (int i = 0; i < ySize; i++) {
1039             yReachesLeft[i] = i;
1040             yReachesRight[i] = i;
1041         }
1042 
1043         int i;
1044         // we check what is the minimum ymin and the maximum ymax reachable by y.
1045         // For that we check every x linked to y to keep the minimal and maximal domain
1046         // bondaries of these xs.
1047         for (int j = 0; j < stampValue; j++) {
1048             i = match3[j];
1049             assert (i >= 0);
1050             assert (i < ySize);
1051             yReachesLeft[i] = Math.min(yReachesLeft[i], xDomain[j].min());
1052             yReachesRight[i] = Math.max(yReachesRight[i], xDomain[j].max());
1053         }
1054         if (debug) {
1055             System.out.println("yReachesLeft ");
1056             for (i = 0; i < yReachesLeft.length; i++)
1057                 System.out.print(yReachesLeft[i] + " ");
1058 
1059             System.out.println("");
1060             System.out.println("yReachesRight ");
1061             for (i = 0; i < yReachesRight.length; i++)
1062                 System.out.print(yReachesRight[i] + " ");
1063 
1064             System.out.println("");
1065         }
1066     }
1067 
1068     //------------------------USED_FOR_REDUCING_DOMAIN--------------------------//
1069 
1070 
1071     private void putToTheEnd(IntVar[] list, int element) {
1072         // I swap the element with the last one before the stamp
1073         // which have nothing to do in the no-more-seen variables
1074         IntVar v1 = list[element];
1075         int stampValue = stamp.value();
1076         list[element] = list[stampValue];
1077         // update the index of the moved element which was behind the stamp value
1078         xNodesHash.put(list[stampValue], element);
1079         // and update the one put to the end
1080         xNodesHash.put(v1, stampValue);
1081         list[stampValue] = v1;
1082     }
1083 
1084     //---------------------------COUNT_BOUND_CONCISTENCY-----------------------//
1085 
1086     private void countBoundConsistency(Store store) {
1087         int[] max_u = new int[ySize];
1088         Arrays.fill(max_u, ySize - 1);
1089 
1090         int[] min_l = new int[ySize];
1091         Arrays.fill(min_l, 0);
1092 
1093         upperCount(max_u);
1094         lowerCount(min_l);
1095 
1096         if (debug) {
1097             System.out.println("max_u ");
1098             for (int aMax_u : max_u)
1099                 System.out.print(aMax_u + " ");
1100 
1101             System.out.println("");
1102             System.out.println("min_l ");
1103             for (int aMin_l : min_l)
1104                 System.out.print(aMin_l + " ");
1105 
1106             System.out.println("");
1107         }
1108         // do the pruning of the domain
1109         for (int i = 0; i < ySize; i++) {
1110             if (debug)
1111                 System.out
1112                     .println("do pruning [" + counters[i].min() + "," + counters[i].max() + "] => [" + min_l[i] + "," + max_u[i] + "]");
1113 
1114             if (yDomain[1][i] != max_u[i] || yDomain[0][i] != min_l[i]) {
1115                 yDomain[0][i] = min_l[i];
1116                 yDomain[1][i] = max_u[i];
1117             }
1118         }
1119         // add the rest of nodes not treated in this pass that was already singleton
1120         if (debug)
1121             System.out.println("increase yDomain with xNodes singleton");
1122 
1123         for (int i = 0; i < xSize; i++) {
1124             if (x[i].singleton()) {
1125                 //Change, check.
1126                 int value = findPosition(x[i].value(), domainHash);
1127                 yDomain[1][value]++;
1128                 yDomain[0][value]++;
1129             }
1130         }
1131 
1132         if (debug)
1133             System.out.println("set yNodes");
1134 
1135         for (int i = 0; i < ySize; i++)
1136             counters[i].domain.in(store.level, counters[i], yDomain[0][i], yDomain[1][i]);
1137 
1138     }
1139 
1140     private void upperCount(int[] max_u) {
1141 
1142         int xIndex, x;
1143         pCount.clear();
1144         xIndex = stampValue - 1;
1145         for (int i = ySize - 1; i >= 0; i--) {
1146             while (xIndex >= 0) {
1147                 x = match2XOrder[xIndex];
1148                 if (match2[x] == i) {
1149                     pCount.add(match1[x]);
1150                     xIndex--;
1151                 } else
1152                     break;
1153             }
1154             max_u[i] = Math.min(yDomain[1][i], pCount.size());
1155             for (int l = 0; l < yDomain[0][i]; l++) {
1156                 assert (!pCount.isEmpty());
1157                 pCount.remove();
1158             }
1159 
1160             // well see how it works for the second part of the condition
1161             while (!pCount.isEmpty() && (pCount.peek() == i)) {
1162                 pCount.remove();
1163             }
1164         }
1165     }
1166 
1167     private void lowerCount(int[] min_l) {
1168         int xIndex, count, x;
1169         pCount.clear();
1170         xIndex = stampValue - 1;
1171         for (int i = ySize - 1; i >= 0; i--) {
1172             count = 0;
1173             while (xIndex >= 0) {
1174                 x = match2XOrder[xIndex];
1175                 if (match2[x] == i) {
1176                     pCount.add(match1[x]);
1177                     xIndex--;
1178                 } else
1179                     break;
1180 
1181             }
1182 
1183             for (int l = 0; l < yDomain[0][i]; l++) {
1184                 assert (!pCount.isEmpty());
1185                 pCount.remove();
1186                 count++;
1187             }
1188 
1189             while ((!pCount.isEmpty()) && (pCount.peek() == i)) {
1190                 pCount.remove();
1191                 count++;
1192             }
1193 
1194             min_l[i] = count;
1195             while ((!pCount.isEmpty()) && (count < yDomain[1][i])) {
1196                 pCount.remove();
1197                 count++;
1198             }
1199         }
1200     }
1201 
1202     //	----------------------------SORT_AND_COMPARATOR--------------------------//
1203 
1204     private void sortXByDomainMin() {
1205         // I need to sort only the part concern, otherwise old values still after
1206         // the stamp value will interfer with the sorting
1207         Arrays.sort(xDomain, 0, stampValue, compareLowerBound);
1208 
1209     }
1210 
1211     //-----------------------INNER CLASSES-----------------------------------//
1212     private static class Component {
1213 
1214         int root;
1215         int rightmostY;
1216         int maxX;
1217 
1218         public Component(int root, int rightmostY, int maxX) {
1219             this.root = root;
1220             this.rightmostY = rightmostY;
1221             this.maxX = maxX;
1222         }
1223     }
1224 
1225 
1226     private static class XDomain extends IntervalDomain {
1227         Var twin;
1228         int index;
1229 
1230         XDomain(Var twin, int min, int max) {
1231             super(min, max);
1232             this.twin = twin;
1233         }
1234     }
1235 
1236     private int findPosition(int value, int[] values) {
1237 
1238         int left = 0;
1239         int right = values.length - 1;
1240 
1241         int position = (left + right) >> 1;
1242 
1243         if (debug) {
1244             System.out.println("Looking for " + value);
1245             for (int v : values)
1246                 System.out.print("val " + v);
1247             System.out.println("");
1248         }
1249 
1250         while (!(left + 1 >= right)) {
1251 
1252             if (debug)
1253                 System.out.println("left " + left + " right " + right + " position " + position);
1254 
1255             if (values[position] > value) {
1256                 right = position;
1257             } else {
1258                 left = position;
1259             }
1260 
1261             position = (left + right) >> 1;
1262 
1263         }
1264 
1265         if (values[left] == value)
1266             return left;
1267 
1268         if (values[right] == value)
1269             return right;
1270 
1271         return -1;
1272 
1273     }
1274 }
1275