1 /*
2  * AtLeast.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.TimeStamp;
37 
38 import java.util.Arrays;
39 import java.util.List;
40 import java.util.concurrent.atomic.AtomicInteger;
41 import java.util.stream.Stream;
42 
43 /**
44  * AtLeast constraint implements the counting over number of occurrences of
45  * a given value in a list of variables. The number of occurrences is
46  * specified by variable value.
47  *
48  * @author Krzysztof Kuchcinski and Radoslaw Szymanek
49  * @version 4.8
50  */
51 
52 public class AtLeast extends PrimitiveConstraint {
53 
54     final static AtomicInteger idNumber = new AtomicInteger(0);
55 
56     /**
57      * It specifies variable idNumber to count the number of occurences of the specified value in a list.
58      */
59     final public int counter;
60 
61     /**
62      * The list of variables which are checked and counted if equal to specified value.
63      */
64     final public IntVar list[];
65 
66     /**
67      * The value to which is any variable is equal to makes the constraint count it.
68      */
69     final public int value;
70 
71     boolean reified = true;
72 
73     /*
74      * Defines first position of the variable that are not considered;
75      * either equal to value or missing the value in their domain.
76      */
77     private TimeStamp<Integer> position;
78 
79     /*
80      * Defines number of variables equal to the value.
81      */
82     private TimeStamp<Integer> equal;
83 
84     /**
85      * It constructs a AtLeast constraint.
86      *
87      * @param value   value which is counted
88      * @param list    variables which equality to val is counted.
89      * @param counter number of variables equal to val.
90      */
AtLeast(IntVar[] list, int counter, int value)91     public AtLeast(IntVar[] list, int counter, int value) {
92 
93         checkInputForNullness("list", list);
94 
95         this.queueIndex = 1;
96         this.numberId = idNumber.incrementAndGet();
97 
98         this.list = Arrays.copyOf(list, list.length);
99         this.counter = counter;
100         this.value = value;
101 
102         setScope(list);
103 
104     }
105 
106     /**
107      * It constructs a AtLeast constraint.
108      *
109      * @param value   value which is counted
110      * @param list    variables which equality to val is counted.
111      * @param counter number of variables equal to val.
112      */
AtLeast(List<? extends IntVar> list, int counter, int value)113     public AtLeast(List<? extends IntVar> list, int counter, int value) {
114         this(list.toArray(new IntVar[list.size()]), counter, value);
115     }
116 
include(Store store)117     @Override public void include(Store store) {
118         position = new TimeStamp<>(store, 0);
119         equal = new TimeStamp<>(store, 0);
120     }
121 
impose(Store store)122     @Override public void impose(Store store) {
123 
124         reified = false;
125 
126         super.impose(store);
127 
128     }
129 
getDefaultConsistencyPruningEvent()130     @Override public int getDefaultConsistencyPruningEvent() {
131         return IntDomain.ANY;
132     }
133 
getDefaultNestedConsistencyPruningEvent()134     @Override protected int getDefaultNestedConsistencyPruningEvent() {
135         return IntDomain.ANY;
136     }
137 
getDefaultNestedNotConsistencyPruningEvent()138     @Override protected int getDefaultNestedNotConsistencyPruningEvent() {
139         return IntDomain.GROUND;
140     }
141 
getDefaultNotConsistencyPruningEvent()142     @Override protected int getDefaultNotConsistencyPruningEvent() {
143         return IntDomain.GROUND;
144     }
145 
consistency(final Store store)146     @Override public void consistency(final Store store) {
147 
148         int numberEq = equal.value();
149 	int numberMayBe = 0;
150 	int start = position.value();
151         for (int i = start; i < list.length; i++) {
152 	    IntVar v = list[i];
153             if (v.domain.contains(value))
154                 if (v.singleton()) {
155                     numberEq++;
156 		    swap(start, i);
157 		    start++;
158 		}
159                 else
160                     numberMayBe++;
161 	    else { // does not have the value in its domain
162                 swap(start, i);
163 		start++;
164 	    }
165         }
166 
167 	if (numberMayBe + numberEq < counter)
168 	    throw Store.failException;
169 	else if (numberEq >= counter) {
170 	    if (!reified) removeConstraint();
171 	}
172 	else if (numberMayBe + numberEq == counter) {
173             for (int i = start; i < list.length; i++) {
174 		IntVar v = list[i];
175 		    if (!v.singleton() && v.domain.contains(value))
176 			v.domain.in(store.level, v, value, value);
177 	    }
178 	    if (!reified)
179 		removeConstraint();
180 	}
181 
182 	equal.update(numberEq);
183 	position.update(start);
184     }
185 
notConsistency(final Store store)186     @Override public void notConsistency(final Store store) {
187 	// at most counter - 1 values
188         int numberEq = equal.value();
189 	int numberMayBe = 0;
190 	int start = position.value();
191         for (int i = start; i < list.length; i++) {
192 	    IntVar v = list[i];
193             if (v.domain.contains(value))
194                 if (v.singleton()) {
195                     numberEq++;
196 		    swap(start, i);
197 		    start++;
198 		}
199                 else
200                     numberMayBe++;
201 	    else { // does not have the value in its domain
202                 swap(start, i);
203 		start++;
204 	    }
205         }
206 
207 	if (numberEq > counter-1)
208 	    throw Store.failException;
209 	else if (numberEq + numberMayBe <= counter-1) {
210 	    if (!reified)
211 		removeConstraint();
212 	}
213 	else if (numberEq == counter-1) {
214 	    for (int i = start; i < list.length; i++) {
215 		IntVar v = list[i];
216 		v.domain.inComplement(store.level, v, value, value);
217 	    }
218 	    if (!reified)
219 		removeConstraint();
220 	}
221 
222     	equal.update(numberEq);
223 	position.update(start);
224     }
225 
swap(int i, int j)226     private void swap(int i, int j) {
227         if (i != j) {
228             IntVar tmp = list[i];
229             list[i] = list[j];
230             list[j] = tmp;
231         }
232     }
233 
satisfied()234     @Override public boolean satisfied() {
235 
236         int numberEq = 0;
237         for (IntVar v : list)
238 	    if (v.singleton(value))
239 		numberEq++;
240 
241 	return numberEq >= counter;
242     }
243 
notSatisfied()244     @Override public boolean notSatisfied() {
245         int numberEq = 0, numberMayBe = 0;
246         for (IntVar v : list) {
247             if (v.domain.contains(value))
248                 if (v.singleton())
249                     numberEq++;
250                 else
251                     numberMayBe++;
252         }
253 
254 	return numberEq + numberMayBe <= counter-1;
255     }
256 
toString()257     @Override public String toString() {
258 
259         StringBuilder result = new StringBuilder(id());
260 
261         result.append(" : AtLeast(").append(value).append(",[");
262 
263         for (int i = 0; i < list.length; i++) {
264             result.append(list[i]);
265             if (i < list.length - 1)
266                 result.append(", ");
267         }
268 
269         result.append("], ").append(counter).append(" )");
270 
271         return result.toString();
272 
273     }
274 
275 }
276