001package org.cpsolver.ifs.model;
002
003import java.lang.reflect.Array;
004import java.util.ArrayList;
005import java.util.HashMap;
006import java.util.List;
007import java.util.Map;
008
009import org.cpsolver.ifs.assignment.Assignment;
010import org.cpsolver.ifs.assignment.DefaultParallelAssignment;
011import org.cpsolver.ifs.assignment.DefaultSingleAssignment;
012import org.cpsolver.ifs.assignment.EmptyAssignment;
013import org.cpsolver.ifs.assignment.context.CanHoldContext;
014import org.cpsolver.ifs.util.IdGenerator;
015
016
017/**
018 * Generic variable. <br>
019 * <br>
020 * Besides a domain of values, a variable also contains information about
021 * assigned value, the value assigned in the best ever found solution and also
022 * the initial value (minimal perturbations problem). It also knows what
023 * constraints are associated with this variable and has a unique id.
024 * 
025 * @see Value
026 * @see Model
027 * @see org.cpsolver.ifs.solver.Solver
028 * 
029 * @author  Tomáš Müller
030 * @version IFS 1.3 (Iterative Forward Search)<br>
031 *          Copyright (C) 2006 - 2014 Tomáš Müller<br>
032 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
033 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
034 * <br>
035 *          This library is free software; you can redistribute it and/or modify
036 *          it under the terms of the GNU Lesser General Public License as
037 *          published by the Free Software Foundation; either version 3 of the
038 *          License, or (at your option) any later version. <br>
039 * <br>
040 *          This library is distributed in the hope that it will be useful, but
041 *          WITHOUT ANY WARRANTY; without even the implied warranty of
042 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
043 *          Lesser General Public License for more details. <br>
044 * <br>
045 *          You should have received a copy of the GNU Lesser General Public
046 *          License along with this library; if not see
047 *          <a href='http://www.gnu.org/licenses/'>http://www.gnu.org/licenses/</a>.
048 * 
049 * @param <V> Variable 
050 * @param <T> Value
051 */
052public class Variable<V extends Variable<V, T>, T extends Value<V, T>> implements Comparable<V> {
053    private static IdGenerator iIdGenerator = new IdGenerator();
054
055    protected long iId = -1;
056    private Model<V, T> iModel = null;
057
058    private T iInitialValue = null; // initial value
059    /** Assigned value */
060    protected T iValue = null; // assigned value
061    @SuppressWarnings("unchecked")
062    private Value<V, T>[] iAssignedValues = (Value<V, T>[])Array.newInstance(Value.class, CanHoldContext.sMaxSize); // assigned values
063    private T iBestValue = null; // best value
064    private long iBestAssignmentIteration = 0;
065    private List<T> iValues = null;
066
067    private T iRecentlyRemovedValue = null;
068    private long iLastIteration = 0;
069    private Object iExtra = null;
070
071    private List<Constraint<V, T>> iConstraints = new ArrayList<Constraint<V, T>>();
072    private List<Constraint<V, T>> iHardConstraints = new ArrayList<Constraint<V, T>>();
073    private List<Constraint<V, T>> iSoftConstraints = new ArrayList<Constraint<V, T>>();
074    private List<VariableListener<T>> iVariableListeners = null;
075
076    private Map<V, List<Constraint<V, T>>> iConstraintVariables = null;
077    protected int iIndex = -1;
078
079    /** Constructor */
080    public Variable() {
081        this(null);
082    }
083
084    /**
085     * Constructor
086     * 
087     * @param initialValue
088     *            initial value (minimal-perturbation problem)
089     */
090    public Variable(T initialValue) {
091        iId = iIdGenerator.newId();
092        setInitialAssignment(initialValue);
093    }
094
095    /** Model, the variable belong to 
096     * @return problem model
097     **/
098    public Model<V, T> getModel() {
099        return iModel;
100    }
101
102    /** Set the model to which the variable belongs to 
103     * @param model problem model
104     **/
105    public void setModel(Model<V, T> model) {
106        iModel = model;
107    }
108    
109    /** Variable's domain, use {@link Variable#values(Assignment)} instead. 
110     * @return all possible values of this variable
111     **/
112    @Deprecated
113    public List<T> values() {
114        return values(new EmptyAssignment<V, T>());
115    }
116
117    /** Variable's domain 
118     * @param assignment current assignment (if the domain is dependent on the current assignment)
119     * @return all possible values of this variable
120     **/
121    public List<T> values(Assignment<V, T> assignment) {
122        return iValues;
123    }
124
125    /** Sets the domain 
126     * @param values variable's domain to cache 
127     **/
128    protected void setValues(List<T> values) {
129        iValues = values;
130    }
131
132    /** True, if the variable's domain is not empty 
133     * @return true if there is at least one value in the domain 
134     **/
135    @Deprecated
136    public boolean hasValues() {
137        return !values().isEmpty();
138    }
139
140    /**
141     * Returns current assignment.
142     * Use {@link Assignment#getValue(Variable)} or {@link Variable#getAssignment(Assignment)} instead.
143     * @return currently assigned value
144     **/
145    @Deprecated
146    public T getAssignment() {
147        return iValue;
148    }
149
150    /**
151     * Returns true if the variable is assigned.
152     * Use {@link Variable#hasAssignment(Assignment)} instead.
153     * @return true if currently assigned
154     **/
155    @Deprecated
156    public boolean hasAssignment() {
157        return iValue != null;
158    }
159    
160    /** Returns current assignment 
161     * @param assignment current assignment
162     * @return currently assigned value
163     **/
164    @SuppressWarnings("unchecked")
165    public T getAssignment(Assignment<V, T> assignment) {
166        return assignment.getValue((V) this);
167    }
168    
169    /** Returns true if the variable is assigned
170     * @param assignment current assignment
171     * @return true if currently assigned
172     **/
173    public boolean hasAssignment(Assignment<V, T> assignment) {
174        return getAssignment(assignment) != null;
175    }
176    
177    /**
178     * Sets current assignment.
179     * BEWARE: Do not use outside of {@link DefaultSingleAssignment}.
180     * @param value current assignment
181     **/
182    @Deprecated
183    public void setAssignment(T value) {
184        iValue = value;
185    }
186    
187    /**
188     * Returns current assignments.
189     * BEWARE: Do not use outside of {@link DefaultParallelAssignment}.
190     * @return currently assigned values
191     **/
192    @Deprecated
193    public Value<V, T>[] getAssignments() {
194        return iAssignedValues;
195    }
196
197    /** Returns initial assignment 
198     * @return initial assignment (for the minimal perturbation problem)
199     **/
200    public T getInitialAssignment() {
201        return iInitialValue;
202    }
203
204    /** Sets initial assignment
205     * @param initialValue initial assignment
206     **/
207    public void setInitialAssignment(T initialValue) {
208        iInitialValue = initialValue;
209        if (iInitialValue != null && iInitialValue.variable() == null)
210            iInitialValue.setVariable(this);
211        if (iModel != null)
212            iModel.invalidateVariablesWithInitialValueCache();
213    }
214
215    /** Returns true if the variable has an initial assignment 
216     * @return true if this variable has an initial assignment  
217     **/
218    public boolean hasInitialAssignment() {
219        return iInitialValue != null;
220    }
221
222    /**
223     * Assign value to this variable. If the variable has already assigned
224     * another value, it is unassigned first. Also, all conflicting values are
225     * unassigned before the given value is assigned to this variable.
226     * Use {@link Assignment#assign(long, Value)} instead.
227     * 
228     * @param iteration
229     *            current iteration
230     * @param value
231     *            the value to be assigned
232     */
233    @Deprecated
234    public void assign(int iteration, T value) {
235        if (iRecentlyRemovedValue != null && iRecentlyRemovedValue.equals(value)) {
236            iRecentlyRemovedValue = null;
237            return;
238        }
239        getModel().getDefaultAssignment().assign(iteration, value);
240    }
241    
242    /**
243     * Unassign value from this variable.
244     * Use {@link Assignment#unassign(long, Variable)} instead.
245     * 
246     * @param iteration
247     *            current iteration
248     */
249    @Deprecated
250    @SuppressWarnings("unchecked")
251    public void unassign(int iteration) {
252        getModel().getDefaultAssignment().unassign(iteration, (V) this);
253    }
254
255    
256    /**
257     * Returns iteration of the last assignment.
258     * Use {@link Assignment#getIteration(Variable)} instead.
259     * @return iteration of the last assignment 
260     **/
261    @Deprecated
262    public long getLastIteration() {
263        return iLastIteration;
264    }
265    
266    /**
267     * Sets iteration of the last assignment.
268     * BEWARE: Do not use outside of {@link DefaultSingleAssignment}.
269     * @param iteration current iteration
270     **/
271    @Deprecated
272    public void setLastIteration(long iteration) {
273        iLastIteration = iteration;
274    }
275
276    /**
277     * A value was assigned to this variable
278     * @param assignment current assignment
279     * @param iteration current iteration
280     * @param value assigned value
281     */
282    public void variableAssigned(Assignment<V, T> assignment, long iteration, T value) {
283        if (iVariableListeners != null)
284            for (VariableListener<T> listener : iVariableListeners) {
285                listener.variableAssigned(assignment, iteration, value);
286            }
287    }
288
289    /**
290     * A value was unassigned from this variable
291     * @param assignment current assignment
292     * @param iteration current iteration
293     * @param oldValue unassigned value
294     */
295    public void variableUnassigned(Assignment<V, T> assignment, long iteration, T oldValue) {
296        if (iVariableListeners != null)
297            for (VariableListener<T> listener : iVariableListeners)
298                listener.variableUnassigned(assignment, iteration, oldValue);
299    }
300
301    /**
302     * Adds a constraint. Called automatically when the constraint is added to
303     * the model, i.e., {@link Model#addConstraint(Constraint)} is called.
304     * 
305     * @param constraint
306     *            added constraint
307     */
308    public void addContstraint(Constraint<V, T> constraint) {
309        iConstraints.add(constraint);
310        if (constraint.isHard()) {
311            iHardConstraints.add(constraint);
312            iConstraintVariables = null;
313        } else
314            iSoftConstraints.add(constraint);
315    }
316
317    /**
318     * Removes a constraint. Called automatically when the constraint is removed
319     * from the model, i.e., {@link Model#removeConstraint(Constraint)} is
320     * called.
321     * 
322     * @param constraint
323     *            added constraint
324     */
325    public void removeContstraint(Constraint<V, T> constraint) {
326        iConstraints.remove(constraint);
327        if (iHardConstraints.contains(constraint)) {
328            iHardConstraints.remove(constraint);
329            iConstraintVariables = null;
330        } else
331            iSoftConstraints.remove(constraint);
332    }
333
334    /** Return the list of constraints associated with this variable 
335     * @return list of constraints associated with this variable
336     **/
337    public List<Constraint<V, T>> constraints() {
338        return iConstraints;
339    }
340
341    /** Return the list of hard constraints associated with this variable 
342     * @return list of hard constraints associated with this variable
343     **/
344    public List<Constraint<V, T>> hardConstraints() {
345        return iHardConstraints;
346    }
347
348    /** Return the list of soft constraints associated with this variable 
349     * @return list of soft (not hard) constraints associated with this variable
350     **/
351    public List<Constraint<V, T>> softConstraints() {
352        return iSoftConstraints;
353    }
354
355    @Override
356    public String toString() {
357        return getName();
358    }
359
360    /** Unique id 
361     * @return variable id
362     **/
363    public long getId() {
364        return iId;
365    }
366
367    @Override
368    public int hashCode() {
369        return (int) iId;
370    }
371
372    /** Variable's name -- for printing purposes 
373     * @return variable name
374     **/
375    public String getName() {
376        return String.valueOf(iId);
377    }
378
379    /** Variable's description -- for printing purposes 
380     * @return variable description
381     **/
382    public String getDescription() {
383        return null;
384    }
385
386    /**
387     * Sets variable's value of the best ever found solution. Called when
388     * {@link Model#saveBest(Assignment)} is called.
389     * @param value a value
390     * @param iteration value's assignment iteration
391     */
392    public void setBestAssignment(T value, long iteration) {
393        iBestValue = value;
394        iBestAssignmentIteration = iteration;
395    }
396
397    /** Returns the value from the best ever found solution. 
398     * @return best assignment 
399     **/
400    public T getBestAssignment() {
401        return iBestValue;
402    }
403
404    /** Returns the iteration when the best value was assigned
405     * @return iteration of the best assignment
406     **/
407    public long getBestAssignmentIteration() {
408        return iBestAssignmentIteration;
409    }
410
411    @Override
412    public int compareTo(V variable) {
413        if (variable == null)
414            return -1;
415        int cmp = getName().compareTo(variable.getName());
416        if (cmp != 0)
417            return cmp;
418        return Double.compare(getId(), variable.getId());
419    }
420
421    @Override
422    public boolean equals(Object o) {
423        if (o == null || !(o instanceof Variable<?, ?>))
424            return false;
425        return getId() == ((Variable<?, ?>) o).getId();
426    }
427
428    /** Adds variable listener 
429     * @param listener a variable listener
430     **/
431    public void addVariableListener(VariableListener<T> listener) {
432        if (iVariableListeners == null)
433            iVariableListeners = new ArrayList<VariableListener<T>>();
434        iVariableListeners.add(listener);
435    }
436
437    /** Removes variable listener 
438     * @param listener a variable listener
439     **/
440    public void removeVariableListener(VariableListener<T> listener) {
441        if (iVariableListeners != null)
442            iVariableListeners.remove(listener);
443    }
444
445    /** Return variable listeners
446     * @return list of variable listeners 
447     **/
448    public List<VariableListener<T>> getVariableListeners() {
449        return iVariableListeners;
450    }
451
452    /**
453     * Extra information to which can be used by an extension (see
454     * {@link org.cpsolver.ifs.extension.Extension}).
455     * @param object extra object
456     */
457    public <X> void setExtra(X object) {
458        iExtra = object;
459    }
460
461    /**
462     * Extra information to which can be used by an extension (see
463     * {@link org.cpsolver.ifs.extension.Extension}).
464     * @return extra object
465     */
466    @SuppressWarnings("unchecked")
467    public <X> X getExtra() {
468        try {
469            return (X) iExtra;
470        } catch (ClassCastException e) {
471            return null;
472        }
473    }
474
475    /**
476     * Permanently remove a value from variable's domain.
477     * The variable should not have this value assigned in any existing assignment.
478     * @param iteration current iteration
479     * @param value value to be removed from this variable's domain
480     **/
481    @SuppressWarnings({ "deprecation", "unchecked" })
482    public void removeValue(long iteration, T value) {
483        if (value.equals(getModel().getDefaultAssignment().getValue((V) this)))
484                getModel().getDefaultAssignment().unassign(iteration, (V) this);
485        if (iValues == null)
486            return;
487        iValues.remove(value);
488        if (iInitialValue != null && iInitialValue.equals(value)) {
489            iInitialValue = null;
490            if (iModel != null)
491                iModel.invalidateVariablesWithInitialValueCache();
492        }
493        if (iVariableListeners != null)
494            for (VariableListener<T> listener : iVariableListeners)
495                listener.valueRemoved(iteration, value);
496        iRecentlyRemovedValue = value;
497    }
498
499    /**
500     * Returns a table of all variables linked with this variable by a
501     * constraint.
502     * 
503     * @return table (variable, constraint)
504     */
505    public Map<V, List<Constraint<V, T>>> constraintVariables() {
506        if (iConstraintVariables == null) {
507            iConstraintVariables = new HashMap<V, List<Constraint<V, T>>>();
508            for (Constraint<V, T> constraint : constraints()) {
509                for (V variable : constraint.variables()) {
510                    if (!variable.equals(this)) {
511                        List<Constraint<V, T>> constraints = iConstraintVariables.get(variable);
512                        if (constraints == null) {
513                            constraints = new ArrayList<Constraint<V, T>>();
514                            iConstraintVariables.put(variable, constraints);
515                        }
516                        constraints.add(constraint);
517                    }
518                }
519            }
520        }
521        return iConstraintVariables;
522    }
523
524    /**
525     * Permanently remove the initial value from the variable's domain -- for
526     * testing MPP
527     */
528    public void removeInitialValue() {
529        if (iInitialValue == null)
530            return;
531        if (iValues == null)
532            return;
533        iValues.remove(iInitialValue);
534        if (iModel != null)
535            iModel.invalidateVariablesWithInitialValueCache();
536        iInitialValue = null;
537    }
538 
539    /**
540     * Unique index of a variable, only to be assigned by {@link Model#addVariable(Variable)}.
541     * @param index an index
542     */
543    public void setIndex(int index) { iIndex = index; }
544
545    /**
546     * Unique index of a variable that was assigned by {@link Model#addVariable(Variable)}.
547     * @return -1 if not in a model
548     */
549    public int getIndex() { return iIndex; }
550}