BooleanEventFunction.java

/* Contributed in the public domain.
 * Licensed to CS GROUP (CS) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * CS 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.orekit.propagation.events.functions;


import org.hipparchus.CalculusFieldElement;
import org.hipparchus.util.FastMath;
import org.orekit.propagation.FieldSpacecraftState;
import org.orekit.propagation.SpacecraftState;

import java.util.List;

/**
 * This class provides AND and OR operations for event functions. This class treats
 * positive values of the g function as true and negative values as false.
 *
 * @since 14.0
 * @author Evan Ward
 * @author Romain Serra
 */
public class BooleanEventFunction implements EventFunction {

    /** Original functions: the operands. */
    private final List<EventFunction> eventFunctions;

    /** The composition function. Should be associative for predictable behavior. */
    private final Operator operator;

    /**
     * Protected constructor with all the parameters.
     *
     * @param eventFunctions    the operands.
     * @param operator     reduction operator to apply to value of the event function of the
     *                     operands.
     */
    private BooleanEventFunction(final List<EventFunction> eventFunctions, final Operator operator) {
        this.eventFunctions = eventFunctions;
        this.operator = operator;
    }

    /**
     * Builds an OR instance.
     * @param eventFunctions functions to reduce
     * @return reduced event function
     */
    public static BooleanEventFunction orCombine(final List<EventFunction> eventFunctions) {
        return new BooleanEventFunction(eventFunctions, Operator.OR);
    }

    /**
     * Builds an AND instance.
     * @param eventFunctions functions to reduce
     * @return reduced event function
     */
    public static BooleanEventFunction andCombine(final List<EventFunction> eventFunctions) {
        return new BooleanEventFunction(eventFunctions, Operator.AND);
    }

    @Override
    public double value(final SpacecraftState s) {
        // can't use stream/lambda here because g(s) throws a checked exception
        // so write out and combine the map and reduce loops
        double ret = Double.NaN; // return value
        boolean first = true;
        for (final EventFunction function : eventFunctions) {
            if (first) {
                ret = function.value(s);
                first = false;
            } else {
                ret = operator.combine(ret, function.value(s));
            }
        }
        // return the result of applying the operator to all operands
        return ret;
    }

    @Override
    public <T extends CalculusFieldElement<T>> T value(final FieldSpacecraftState<T> fieldState) {
        // can't use stream/lambda here because g(s) throws a checked exception
        // so write out and combine the map and reduce loops
        T ret = fieldState.getDate().getField().getZero().newInstance(Double.NaN); // return value
        boolean first = true;
        for (final EventFunction function : eventFunctions) {
            if (first) {
                ret = function.value(fieldState);
                first = false;
            } else {
                ret = operator.combine(ret, function.value(fieldState));
            }
        }
        // return the result of applying the operator to all operands
        return ret;
    }

    @Override
    public boolean dependsOnTimeOnly() {
        return eventFunctions.stream().allMatch(EventFunction::dependsOnTimeOnly);
    }

    @Override
    public boolean dependsOnMainVariablesOnly() {
        return eventFunctions.stream().allMatch(EventFunction::dependsOnMainVariablesOnly);
    }

    /** Local class for operator. */
    private enum Operator {

        /** And operator. */
        AND() {
            double combine(final double g1, final double g2) {
                return FastMath.min(g1, g2);
            }

            <T extends CalculusFieldElement<T>> T combine(final T g1, final T g2) {
                return FastMath.min(g1, g2);
            }
        },

        /** Or operator. */
        OR() {
            double combine(final double g1, final double g2) {
                return FastMath.max(g1, g2);
            }

            <T extends CalculusFieldElement<T>> T combine(final T g1, final T g2) {
                return FastMath.max(g1, g2);
            }
        };

        /** Combine two functions evaluations.
         * @param g1 first evaluation
         * @param g2 second evaluation
         * @return combined evaluation
         */
        abstract double combine(double g1, double g2);

        /** Combine two functions evaluations.
         * @param g1 first evaluation
         * @param g2 second evaluation
         * @param <T> field type
         * @return combined evaluation
         */
        abstract <T extends CalculusFieldElement<T>> T combine(T g1, T g2);
    }

}