MultiLinear.java

/*
 * Copyright (C) 2021 INRAE AgroClim
 *
 * This file is part of Indicators.
 *
 * Indicators is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Indicators is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with Indicators. If not, see <http://www.gnu.org/licenses/>.
 */
package fr.inrae.agroclim.indicators.model.function.normalization;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.StringJoiner;
import java.util.stream.Stream;

import jakarta.xml.bind.annotation.XmlElement;
import jakarta.xml.bind.annotation.XmlRootElement;
import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

/**
 * Normalize with several Linear function applied to intervals.
 *
 * Last change $Date$
 *
 * @author $Author$
 * @version $Revision$
 */
@XmlRootElement
@ToString
public final class MultiLinear extends NormalizationFunction {

    /**
     * UUID for Serializable.
     */
    private static final long serialVersionUID = 4872847709393688047L;

    /**
     * Linear functions on the related intervals.
     */
    @Getter
    @Setter
    @XmlElement(name = "interval")
    private List<MultiLinearInterval> intervals = new ArrayList<>();

    /**
     * Constructor with no args for Serializable.
     */
    public MultiLinear() {
        // Do nothing
    }

    @Override
    public MultiLinear clone() {
        final MultiLinear clone = new MultiLinear();
        if (intervals != null && !intervals.isEmpty()) {
            clone.setIntervals(new ArrayList<>());
            intervals.forEach(interval -> clone.getIntervals().add(interval.clone()));
        }
        return clone;
    }

    @Override
    public String getFormulaMathML() {
        final StringJoiner sj = new StringJoiner("\n");
        sj.add("<mfenced open=\"{\" close=\"\">");
        sj.add(" <mtable>");
        final Stream<MultiLinearInterval> stream;
        if (intervals != null) {
            stream = intervals.stream();
        } else {
            final MultiLinearInterval interval = new MultiLinearInterval();
            interval.setLinear(new Linear());
            stream = Arrays.asList(interval).stream();
        }
        stream.filter(interval -> interval.getLinear() != null)
        .forEach(interval -> {
            sj.add("  <mtr>");
            sj.add("   <mtd>");
            sj.add(interval.getLinear().getFormulaMathML());
            sj.add("   </mtd>");
            sj.add("   <mtd>");
            sj.add("     <mn>x</mn><mo>&isin;</mo><mn>[");
            final Double iMin = interval.getMin();
            final Double iMax = interval.getMax();
            if (iMin != null) {
                sj.add(iMin.toString());
            } else {
                sj.add("-∞");
            }
            sj.add("; ");
            if (iMax != null) {
                sj.add(iMax.toString() + "[;");
            } else {
                sj.add("+∞];");
            }
            sj.add("</mn>");
            sj.add("   </mtd>");
            sj.add("  </mtr>");
        });
        sj.add(" </mtable>");
        sj.add("</mfenced>");
        return sj.toString();
    }

    @Override
    public double normalize(final double value) {
        if (intervals == null || intervals.isEmpty()) {
            return 0;
        }
        for (final MultiLinearInterval interval : intervals) {
            if (interval.matches(value)) {
                return interval.getLinear().normalize(value);
            }
        }
        return 0;
    }

}