CrossEqual.java

/* Copyright 2015 Laurent COCAULT
 * Licensed to Laurent COCAULT under one or more contributor license agreements.
 * See the NOTICE file distributed with this work for additional information
 * regarding copyright ownership. Laurent COCAULT licenses this file to You
 * under the Apache License, Version 2.0 (the "License"); you may not use this
 * file except in compliance with the License.  You may obtain a copy of the
 * License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.csp.constraint.model.general;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.csp.constraint.model.UnsizedConstraint;
import org.csp.constraint.model.Value;
import org.csp.constraint.model.Variable;

/**
 * Constraint specifying that associate variables from two different lists. When
 * the a variable of one list has the expected value, the variable of the same
 * index of the other list has the other expected value. Example : let's have
 * two array of variables (A, B, C) and (D, E, F). The constraint can specify
 * that when a variable of the first array has a value V1, the corresponding
 * variable of the second array has a value V2. Suppose that during a
 * propagation B take V1 as a value. Then the constraint will value E=V2.
 */
public class CrossEqual<T> extends UnsizedConstraint<T> {

    /** Left array. */
    private List<Variable<T>> left_;

    /** Left value. */
    private Value<T> leftValue_;

    /** Right array. */
    private List<Variable<T>> right_;

    /** Right value. */
    private Value<T> rightValue_;

    /**
     * Constructor.
     * @param leftArray
     *            First array
     * @param leftValue
     *            Expected value for the first array
     * @param rightArray
     *            Second array
     * @param rightValue
     *            Expected value for the second array
     */
    public CrossEqual(final List<Variable<T>> leftArray,
            final Value<T> leftValue, final List<Variable<T>> rightArray,
            final Value<T> rightValue) {
        super("CROSS_EQUAL", new ArrayList<Variable<T>>());
        // Initialize attributes
        left_ = leftArray;
        leftValue_ = leftValue;
        right_ = rightArray;
        rightValue_ = rightValue;
        // Both vector variables are implied in the constraint
        addVariables(leftArray);
        addVariables(rightArray);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void propagate() {
        // Propagate in both ways
        propagate(left_, leftValue_, right_, rightValue_);
        propagate(right_, rightValue_, left_, leftValue_);
    }

    /**
     * Propagate constraint in one way : check "from" variables and deduce
     * possible values for "to" variables.
     * @param from
     *            List of variables at the origin of the propagation
     * @param fromValue
     *            Value at the origin of the propagation
     * @param to
     *            List of variables to update
     * @param toValue
     *            Value associated to the fromValue
     */
    protected void propagate(final List<Variable<T>> from,
            final Value<T> fromValue, final List<Variable<T>> to,
            final Value<T> toValue) {

        // Iterate with the index to find the corresponding variable in "to"
        for (int index = 0; index < from.size(); index++) {

            // Get the "from" variable
            final Variable<T> var = from.get(index);

            if (var.isBound() && var.getValue().equals(fromValue)) {

                // When the "from" variable is bound with the expected value,
                // the corresponding "to" variable should also be bound.
                if (to.get(index).setValue(toValue)) {
                    addRecentChangedVariable(to.get(index));
                }

            } else if (!var.contains(fromValue)) {

                // When the expected value is no more in the "from" variable
                // domain, the corresponding "to" value must not be in the
                // domain of the "to" variable.
                if (to.get(index).removeValue(toValue)) {
                    addRecentChangedVariable(to.get(index));
                }

            }
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public String toString() {

        // String builder
        final StringBuilder builder = new StringBuilder();

        // Display the constraint
        builder.append("CROSS_EQUAL(" + leftValue_ + " in (");
        Iterator<Variable<T>> vars = left_.iterator();
        while (vars.hasNext()) {
            // Display every variable
            builder.append(vars.next().getName());
            if (vars.hasNext()) {
                // Next variable
                builder.append(", ");
            } else {
                // Last variable
                builder.append(") equals " + rightValue_ + " in (");
            }
        }
        vars = right_.iterator();
        while (vars.hasNext()) {
            // Display every variable
            builder.append(vars.next().getName());
            if (vars.hasNext()) {
                // Next variable
                builder.append(", ");
            } else {
                // Last variable
                builder.append("))");
            }
        }

        return builder.toString();
    }

}