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