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