EqualToSum.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.integer;

import org.csp.constraint.model.TernaryConstraint;

/**
 * Constraint specifying that one specific integer variable is the sum of two
 * other integer variables.
 */
public class EqualToSum extends TernaryConstraint<IntValue> {

    /**
     * Constructor of the constraint "FIRST + SECOND = SUM".
     * @param first
     *            First variable
     * @param second
     *            Second variable
     * @param sum
     *            Sum of the two other variables
     */
    public EqualToSum(final IntVar first, final IntVar second, final IntVar sum) {
        super(first.getName() + "+" + second.getName() + "=" + sum.getName(),
                first, second, sum);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void propagate() {

        // The sum minimum is the sum of the other variables minimum and the
        // sum maximum is the sum of the other variables maximum.
        final IntValue sumMin = IntValue.sum(getFirst().getMinValue(),
                getSecond().getMinValue());
        final boolean sumMinChanged = getSum().reduceWithMinValue(sumMin);
        final IntValue sumMax = IntValue.sum(getFirst().getMaxValue(),
                getSecond().getMaxValue());
        final boolean sumMaxChanged = getSum().reduceWithMaxValue(sumMax);
        if (sumMaxChanged || sumMinChanged) {
            addRecentChangedVariable(getSum());
        }

        // Propagate the constraint on the first variable
        propagateOnArgument(getFirst(), getSecond(), getSum());

        // Propagate the constraint on the second variable
        propagateOnArgument(getSecond(), getFirst(), getSum());
    }

    /**
     * Propagate the constraint on an argument. The propagation algorithm is the
     * same on the first or the second argument of the constraint: the current
     * method is therefore applied twice with a permutation of the variables.
     * @param argument
     *            Argument on which to propagate the constraint
     * @param other
     *            Other argument in the sum
     * @param sum
     *            Sum of the two arguments
     */
    private void propagateOnArgument(final IntVar argument, final IntVar other,
            final IntVar sum) {

        // The argument maximum shall not be greater than the difference between
        // the "sum" maximum and the "other" minimum.
        final IntValue newMax = IntValue.diff(sum.getMaxValue(),
                other.getMinValue());
        final boolean maxChanged = argument.reduceWithMaxValue(newMax);

        // The argument minimum shall not be lesser than the difference between
        // the "sum" minimum and the "other" maximum.
        final IntValue newMin = IntValue.diff(sum.getMinValue(),
                other.getMaxValue());
        final boolean minChanged = argument.reduceWithMinValue(newMin);

        if (maxChanged || minChanged) {
            addRecentChangedVariable(argument);
        }
    }

    /**
     * Get the first argument of the constraint.
     * @return First argument
     */
    protected IntVar getFirst() {
        return (IntVar) getFirstVariable();
    }

    /**
     * Get the second argument of the constraint.
     * @return Second argument
     */
    protected IntVar getSecond() {
        return (IntVar) getSecondVariable();
    }

    /**
     * Get the sum.
     * @return Sum (third variable)
     */
    protected IntVar getSum() {
        return (IntVar) getThirdVariable();
    }

}