001package org.cpsolver.ifs.assignment;
002
003import java.util.ArrayList;
004import java.util.Collection;
005import java.util.List;
006
007import org.cpsolver.ifs.assignment.context.AssignmentContext;
008import org.cpsolver.ifs.assignment.context.AssignmentContextHolder;
009import org.cpsolver.ifs.assignment.context.AssignmentContextReference;
010import org.cpsolver.ifs.assignment.context.HasAssignmentContext;
011import org.cpsolver.ifs.constant.ConstantVariable;
012import org.cpsolver.ifs.criteria.Criterion;
013import org.cpsolver.ifs.model.Constraint;
014import org.cpsolver.ifs.model.GlobalConstraint;
015import org.cpsolver.ifs.model.Model;
016import org.cpsolver.ifs.model.Value;
017import org.cpsolver.ifs.model.Variable;
018
019
020/**
021 * An abstract implementation of an {@link Assignment} object. It contains an instance of
022 * a given assignment context holder (see {@link AssignmentContextHolder}) and 
023 * implements the assignment logic. But the actual process of storing and retrieving values
024 * is left on the assignment implementation.
025 * 
026 * @see Assignment
027 * 
028 * @version IFS 1.3 (Iterative Forward Search)<br>
029 *          Copyright (C) 2014 Tomas Muller<br>
030 *          <a href="mailto:muller@unitime.org">muller@unitime.org</a><br>
031 *          <a href="http://muller.unitime.org">http://muller.unitime.org</a><br>
032 * <br>
033 *          This library is free software; you can redistribute it and/or modify
034 *          it under the terms of the GNU Lesser General Public License as
035 *          published by the Free Software Foundation; either version 3 of the
036 *          License, or (at your option) any later version. <br>
037 * <br>
038 *          This library is distributed in the hope that it will be useful, but
039 *          WITHOUT ANY WARRANTY; without even the implied warranty of
040 *          MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
041 *          Lesser General Public License for more details. <br>
042 * <br>
043 *          You should have received a copy of the GNU Lesser General Public
044 *          License along with this library; if not see <a href='http://www.gnu.org/licenses'>http://www.gnu.org/licenses</a>.
045 * @param <V> Variable
046 * @param <T> Value
047 **/
048public abstract class AssignmentAbstract<V extends Variable<V, T>, T extends Value<V, T>> implements Assignment<V, T> {
049    protected AssignmentContextHolder<V, T> iContexts;
050    protected boolean iHasInitialzedContext = false;
051    
052    /**
053     * Constructor
054     * @param contexts an instance of the assignment context holder
055     */
056    public AssignmentAbstract(AssignmentContextHolder<V, T> contexts) {
057        iContexts = contexts;
058    }
059    
060    /**
061     * Checks if the variable is {@link ConstantVariable}, returns {@link AssignmentAbstract#getValueInternal(Variable)}
062     * if the variable is not a constant.
063     */
064    @Override
065    @SuppressWarnings("unchecked")
066    public T getValue(V variable) {
067        if (variable instanceof ConstantVariable<?> && ((ConstantVariable<?>)variable).isConstant())
068            return ((ConstantVariable<T>)variable).getConstantValue();
069        return getValueInternal(variable);
070    }
071
072    /**
073     * Returns assignment of a variable, null if not assigned. To be implemented.
074     * @param variable a variable in question
075     * @return assigned value
076     **/
077    protected abstract T getValueInternal(V variable);
078    
079    /**
080     * Sets an assignment to a variable (unassigns a variable if the given value is null). To be implemented.
081     * @param iteration current iteration
082     * @param variable a variable to be assigned
083     * @param value new assignment, null if to be unassigned
084     **/
085    protected abstract void setValueInternal(long iteration, V variable, T value);
086    
087    /** Assigns a variable with the given value. All the appropriate classes are notified about the change.
088     * It is using {@link AssignmentAbstract#setValueInternal(long, Variable, Value)} to store the new 
089     * assignment.
090     * @param iteration current iteration
091     * @param variable a variable
092     * @param value one of its values, null if the variable is to be unassigned
093     * @return previous assignment of the variable 
094     **/
095    @SuppressWarnings("unchecked")
096    protected T assign(long iteration, V variable, T value) {
097        if (variable instanceof ConstantVariable<?> && ((ConstantVariable<?>)variable).isConstant())
098            return ((ConstantVariable<T>)variable).getConstantValue();
099
100        assert variable.getModel() != null && (value == null || variable.equals(value.variable()));
101        Model<V, T> model = variable.getModel();
102        
103        // ensure all model, criterion, and constraint assignment contexts are initialized before changing the assignment value
104        ensureInitializedContext(variable);
105        
106        // unassign old value, if assigned
107        T old = getValueInternal(variable);
108        if (old != null) {
109            if (model != null)
110                model.beforeUnassigned(this, iteration, old);
111            setValueInternal(iteration, variable, null);
112            for (Constraint<V, T> constraint : variable.constraints())
113                constraint.unassigned(this, iteration, old);
114            if (model != null)
115                for (GlobalConstraint<V, T> constraint : model.globalConstraints())
116                    constraint.unassigned(this, iteration, old);
117            variable.variableUnassigned(this, iteration, old);
118            if (model != null)
119                model.afterUnassigned(this, iteration, old);
120        }
121        
122        // assign new value, if provided
123        if (value != null) {
124            if (model != null)
125                model.beforeAssigned(this, iteration, value);
126            setValueInternal(iteration, variable, value);
127            for (Constraint<V, T> constraint : variable.constraints())
128                constraint.assigned(this, iteration, value);
129            if (model != null)
130                for (GlobalConstraint<V, T> constraint : model.globalConstraints())
131                    constraint.assigned(this, iteration, value);
132            variable.variableAssigned(this, iteration, value);
133            if (model != null)
134                model.afterAssigned(this, iteration, value);
135        }
136        
137        // return old value
138        return old;
139    }
140    
141    @Override
142    public T assign(long iteration, T value) {
143        return assign(iteration, value.variable(), value);
144    }
145    
146    @Override
147    public T unassign(long iteration, V variable) {
148        return assign(iteration, variable, null);
149    }
150    
151    @Override
152    public int nrAssignedVariables() {
153        return assignedVariables().size();
154    }
155    
156    @Override
157    public Collection<T> assignedValues() {
158        List<T> values = new ArrayList<T>();
159        for (V variable: assignedVariables())
160            values.add(getValueInternal(variable));
161        return values;
162    }
163
164    @Override
165    public Collection<V> unassignedVariables(Model<V, T> model) {
166        List<V> unassigned = new ArrayList<V>();
167        for (V variable: model.variables())
168            if (getValue(variable) == null)
169                unassigned.add(variable);
170        return unassigned;
171    }
172
173    @Override
174    public int nrUnassignedVariables(Model<V, T> model) {
175        return model.variables().size() - nrAssignedVariables();
176    }
177
178    @Override
179    public <C extends AssignmentContext> C getAssignmentContext(AssignmentContextReference<V, T, C> reference) {
180        return iContexts.getAssignmentContext(this, reference);
181    }
182    
183    @Override
184    public <C extends AssignmentContext> void clearContext(AssignmentContextReference<V, T, C> reference) {
185        iContexts.clearContext(reference);
186    }
187    
188    @Override
189    public int getIndex() {
190        return -1;
191    }
192    
193    /**
194     * Ensure that the model, all criteria, all global constraints and all the related constraints have their assignment contexts initialized.
195     * @param variable a variable to be changed
196     */
197    @SuppressWarnings("unchecked")
198    protected void ensureInitializedContext(V variable) {
199        if (!iHasInitialzedContext && variable.getModel() != null) {
200            if (variable.getModel() instanceof HasAssignmentContext)
201                iContexts.getAssignmentContext(this, ((HasAssignmentContext<V, T, ?>)variable.getModel()).getAssignmentContextReference());
202            for (Criterion<V, T> criterion: variable.getModel().getCriteria())
203                if (criterion instanceof HasAssignmentContext)
204                    iContexts.getAssignmentContext(this, ((HasAssignmentContext<V, T, ?>)criterion).getAssignmentContextReference());
205            for (GlobalConstraint<V, T> constraint: variable.getModel().globalConstraints())
206                if (constraint instanceof HasAssignmentContext)
207                    iContexts.getAssignmentContext(this, ((HasAssignmentContext<V, T, ?>)constraint).getAssignmentContextReference());
208            iHasInitialzedContext = true;
209        }
210        for (Constraint<V, T> constraint: variable.constraints())
211            if (constraint instanceof HasAssignmentContext)
212                iContexts.getAssignmentContext(this, ((HasAssignmentContext<V, T, ?>)constraint).getAssignmentContextReference());
213    }
214}