1 /*
2  * Match.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.set.constraints;
32 
33 import org.jacop.api.SatisfiedPresent;
34 import org.jacop.constraints.Constraint;
35 import org.jacop.core.*;
36 import org.jacop.set.core.SetDomain;
37 import org.jacop.set.core.SetVar;
38 
39 import java.util.Arrays;
40 import java.util.concurrent.atomic.AtomicInteger;
41 import java.util.stream.Stream;
42 
43 /**
44  * This constraint matches the elements of the given set variable
45  * onto a list of integer variables.
46  *
47  * @author Radoslaw Szymanek, Krzysztof Kuchcinski, and Robert Åkemalm
48  * @version 4.8
49  */
50 
51 public class Match extends Constraint implements SatisfiedPresent {
52 
53     static AtomicInteger idNumber = new AtomicInteger(0);
54 
55     /**
56      * It specifies a set variable whose values are being matched against integer variables
57      * from the list.
58      */
59     public SetVar a;
60 
61     /**
62      * It specifies the list of integer variables which value is being matched against
63      * elements from a set variable a.
64      */
65     public IntVar[] list;
66 
67     /**
68      * It constructs a match constraint to connect the value of set variable a
69      * to the values of integer variables provided in the list.
70      *
71      * @param a    set variable that is restricted to be equal to a set created from values specified by integer variables form the list.
72      * @param list of integer variables that is restricted to have the same elements as set variable a.
73      */
74 
Match(SetVar a, IntVar[] list)75     public Match(SetVar a, IntVar[] list) {
76 
77         checkInputForNullness(new String[] {"a", "list"}, new Object[][] {{a}, list});
78 
79         this.numberId = idNumber.incrementAndGet();
80         this.a = a;
81         this.list = Arrays.copyOf(list, list.length);
82 
83         setScope(Stream.concat(Stream.of(a), Arrays.stream(list)));
84 
85     }
86 
consistency(Store store)87     @Override public void consistency(Store store) {
88 
89         /**
90          * It specifies the consistency rules for constraint
91          * match(list, a) where list is a list of intVar and a is a setVar.
92          *
93          * list is lexicographically ordered elements of a.
94          *
95          * [a, b, c, e, f] = {a, b, c, e, f}
96          *
97          * #A = list.length
98          *
99          * each element el in A.glb must occurr in one of the intvar in list.
100          * elPos is lexicographical position of el in A.glb
101          *
102          * element el can only occur within an interval of intvars list[elPos]..list[list.length - (#A.glb-elPos)]
103          *
104          * every element elU in A.lub that is not in A.glb can if added to glb will end up at position posElU
105          * and for this element we can also say that it can only occur in list[posElU]..list[list.length - (1+#A.glb-posElU)]
106          *
107          * for all l[i], D(l[i]) must be in A.lub
108          *
109          * A.lub = A.lub /\ ( \/ D(i) ).
110          *
111          */
112 
113         a.domain.inCardinality(store.level, a, list.length, list.length);
114 
115         if (a.domain.glb().getSize() == list.length) {
116 
117             ValueEnumeration ve = a.domain.glb().valueEnumeration();
118             int el;
119             for (int i = 0; i < list.length; i++) {
120                 el = ve.nextElement();
121                 list[i].domain.in(store.level, list[i], el, el);
122             }
123             a.domain.inLUB(store.level, a, a.domain.glb());
124 
125         } else if (a.domain.lub().getSize() == list.length) {
126 
127             ValueEnumeration ve = a.domain.lub().valueEnumeration();
128             int el;
129             for (int i = 0; i < list.length; i++) {
130                 el = ve.nextElement();
131                 list[i].domain.in(store.level, list[i], el, el);
132             }
133             a.domain.inGLB(store.level, a, a.domain.lub());
134 
135         } else {
136 
137             IntDomain glbA = a.domain.glb();
138             IntDomain lubA = a.domain.lub();
139 
140             int sizeOfaGLB = glbA.getSize();
141             int sizeOfaLUB = lubA.getSize();
142 
143             // glbA, lubA => list[i]
144             for (int i = 0; i < list.length; i++) {
145 
146                 list[i].domain.in(store.level, list[i], lubA);
147 
148                 int minValue = lubA.getElementAt(i);
149 
150                 if (i >= list.length - sizeOfaGLB) {
151                     // -1 since indexing of arrays starts from 0.
152                     int minValueFromGLB = glbA.getElementAt(sizeOfaGLB - list.length + i);
153                     if (minValueFromGLB > minValue)
154                         minValue = minValueFromGLB;
155                 }
156 
157                 list[i].domain.inMin(store.level, list[i], minValue);
158 
159                 int maxValue = lubA.getElementAt(sizeOfaLUB - list.length + i);
160 
161                 if (i < sizeOfaGLB) {
162                     int maxValueFromGLB = glbA.getElementAt(i);
163                     if (maxValueFromGLB < maxValue)
164                         maxValue = maxValueFromGLB;
165                 }
166 
167                 list[i].domain.inMax(store.level, list[i], maxValue);
168 
169             }
170 
171             IntDomain lubFromList = list[0].domain.cloneLight();
172             for (int i = 0; i < list.length; i++) {
173                 if (list[i].singleton())
174                     a.domain.inGLB(store.level, a, list[i].value());
175                 if (i > 0)
176                     lubFromList.unionAdapt(list[i].domain);
177             }
178             a.domain.inLUB(store.level, a, lubFromList);
179 
180         }
181 
182     }
183 
getConsistencyPruningEvent(Var var)184     @Override public int getConsistencyPruningEvent(Var var) {
185 
186         // If consistency function mode
187         if (consistencyPruningEvents != null) {
188             Integer possibleEvent = consistencyPruningEvents.get(var);
189             if (possibleEvent != null)
190                 return possibleEvent;
191         }
192 
193         if (var == a)
194             return SetDomain.ANY;
195         else
196             return IntDomain.ANY;
197     }
198 
getDefaultConsistencyPruningEvent()199     @Override public int getDefaultConsistencyPruningEvent() {
200         throw new IllegalStateException("Not implemented as more precise version exist.");
201     }
202 
satisfied()203     @Override public boolean satisfied() {
204 
205         if (!grounded())
206             return false;
207 
208         if (a.domain.glb().getSize() == list.length) {
209 
210             ValueEnumeration ve = a.domain.glb().valueEnumeration();
211 
212             for (int i = 0; i < list.length; i++) {
213                 if (ve.nextElement() != list[i].value())
214                     return false;
215             }
216 
217             return true;
218 
219         } else {
220             return false;
221         }
222 
223     }
224 
toString()225     @Override public String toString() {
226 
227         StringBuffer ret = new StringBuffer(id());
228         ret.append(" : Match(" + a + ", [ ");
229         for (Var fdv : list)
230             ret.append(fdv + " ");
231         ret.append("] )");
232         return ret.toString();
233 
234     }
235 
236 }
237