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 Tomáš Müller<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 (old.equals(value)) return old;
110            if (model != null)
111                model.beforeUnassigned(this, iteration, old);
112            setValueInternal(iteration, variable, null);
113            for (Constraint<V, T> constraint : variable.constraints())
114                constraint.unassigned(this, iteration, old);
115            if (model != null)
116                for (GlobalConstraint<V, T> constraint : model.globalConstraints())
117                    constraint.unassigned(this, iteration, old);
118            variable.variableUnassigned(this, iteration, old);
119            if (model != null)
120                model.afterUnassigned(this, iteration, old);
121        }
122        
123        // assign new value, if provided
124        if (value != null) {
125            if (model != null)
126                model.beforeAssigned(this, iteration, value);
127            setValueInternal(iteration, variable, value);
128            for (Constraint<V, T> constraint : variable.constraints())
129                constraint.assigned(this, iteration, value);
130            if (model != null)
131                for (GlobalConstraint<V, T> constraint : model.globalConstraints())
132                    constraint.assigned(this, iteration, value);
133            variable.variableAssigned(this, iteration, value);
134            if (model != null)
135                model.afterAssigned(this, iteration, value);
136        }
137        
138        // return old value
139        return old;
140    }
141    
142    @Override
143    public T assign(long iteration, T value) {
144        return assign(iteration, value.variable(), value);
145    }
146    
147    @Override
148    public T unassign(long iteration, V variable) {
149        return assign(iteration, variable, null);
150    }
151    
152    @Override
153    public T unassign(long iteration, V variable, T value) {
154        T current = getValue(variable);
155        if (current == null && value == null) {
156            return current;
157        } else if (current != null && current.equals(value)) {
158            return current;
159        } else {
160            return assign(iteration, variable, null);
161        }
162    }
163    
164    @Override
165    public int nrAssignedVariables() {
166        return assignedVariables().size();
167    }
168    
169    @Override
170    public Collection<T> assignedValues() {
171        List<T> values = new ArrayList<T>();
172        for (V variable: assignedVariables())
173            values.add(getValueInternal(variable));
174        return values;
175    }
176
177    @Override
178    public Collection<V> unassignedVariables(Model<V, T> model) {
179        List<V> unassigned = new ArrayList<V>();
180        for (V variable: model.variables())
181            if (getValue(variable) == null)
182                unassigned.add(variable);
183        return unassigned;
184    }
185
186    @Override
187    public int nrUnassignedVariables(Model<V, T> model) {
188        return model.variables().size() - nrAssignedVariables();
189    }
190
191    @Override
192    public <C extends AssignmentContext> C getAssignmentContext(AssignmentContextReference<V, T, C> reference) {
193        return iContexts.getAssignmentContext(this, reference);
194    }
195    
196    @Override
197    public <C extends AssignmentContext> void clearContext(AssignmentContextReference<V, T, C> reference) {
198        iContexts.clearContext(reference);
199    }
200    
201    @Override
202    public int getIndex() {
203        return -1;
204    }
205    
206    /**
207     * Ensure that the model, all criteria, all global constraints and all the related constraints have their assignment contexts initialized.
208     * @param variable a variable to be changed
209     */
210    @SuppressWarnings("unchecked")
211    protected void ensureInitializedContext(V variable) {
212        if (!iHasInitialzedContext && variable.getModel() != null) {
213            if (variable.getModel() instanceof HasAssignmentContext)
214                iContexts.getAssignmentContext(this, ((HasAssignmentContext<V, T, ?>)variable.getModel()).getAssignmentContextReference());
215            for (Criterion<V, T> criterion: variable.getModel().getCriteria())
216                if (criterion instanceof HasAssignmentContext)
217                    iContexts.getAssignmentContext(this, ((HasAssignmentContext<V, T, ?>)criterion).getAssignmentContextReference());
218            for (GlobalConstraint<V, T> constraint: variable.getModel().globalConstraints())
219                if (constraint instanceof HasAssignmentContext)
220                    iContexts.getAssignmentContext(this, ((HasAssignmentContext<V, T, ?>)constraint).getAssignmentContextReference());
221            iHasInitialzedContext = true;
222        }
223        for (Constraint<V, T> constraint: variable.constraints())
224            if (constraint instanceof HasAssignmentContext)
225                iContexts.getAssignmentContext(this, ((HasAssignmentContext<V, T, ?>)constraint).getAssignmentContextReference());
226    }
227}