MathUtils.java

/*
 * Copyright (c) 2021 Mārtiņš Avots (Martins Avots) and others
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0, or the MIT License,
 * which is available at https://spdx.org/licenses/MIT.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR MIT
 */
package net.splitcells.dem.utils;

import net.splitcells.dem.data.set.list.List;
import net.splitcells.dem.data.set.list.Lists;

import static net.splitcells.dem.data.set.list.Lists.list;
import static net.splitcells.dem.data.set.list.Lists.listWithValuesOf;
import static net.splitcells.dem.utils.ConstructorIllegal.constructorIllegal;
import static org.assertj.core.api.Assertions.assertThat;

public final class MathUtils {

    public static List<List<Integer>> sumsForTarget(int target, List<Integer> sumComponents) {
        return sumsForTarget(target, sumComponents, list());
    }

    private static List<List<Integer>> sumsForTarget(int target, List<Integer> sumComponents
            , List<Integer> currentResult) {
        final List<List<Integer>> nextResults = list();
        final var sum = currentResult.stream().reduce(Integer::sum).orElse(0);
        sumComponents.stream()
                .filter(c -> sum + c <= target)
                .map(c -> listWithValuesOf(currentResult).withAppended(c))
                .map(c -> sumsForTarget(target, sumComponents, c))
                .forEach(nextResults::addAll);
        if (nextResults.isEmpty()) {
            return list(currentResult);
        }
        return nextResults;
    }

    public static int modulus(int dividend, int divisor) {
        return Math.floorMod(dividend, divisor);
    }

    public static int floorToInt(double arg) {
        return (int) Math.floor(arg);
    }

    public static int roundToInt(double arg) {
        return Math.toIntExact(Math.round(arg));
    }

    public static double intToDouble(int arg) {
        return Double.valueOf(arg);
    }

    public static float doubleToFloat(double arg) {
        return Double.valueOf(arg).floatValue();
    }

    /**
     * TODO RENAME
     *
     * @return
     */
    public static boolean acceptable(double value, double target) {
        return acceptable(value, target, 0.1d);
    }

    /**
     * TODO RENAME
     *
     * <p>
     * This function calculates whether the error in a calculated {@code value},
     * is bigger than a given {@code maxDeviation} in percent.
     * </p>
     *
     * @param value        This is the value, that is computed.
     * @param target       This is the value, that is tried to be computed.
     * @param maxDeviation Allowed deviation from {@code target} in percent of the {@code target}.
     * @return Returns {@code true}, if the deviation of the {@code value} from the {@code target} is acceptable.
     */
    public static boolean acceptable(double value, double target, double maxDeviation) {
        return value < target + (1 + maxDeviation) && value > target * (1 - maxDeviation);
    }

    public static double distance(double a, double b) {
        if (a < b) {
            return Math.abs(b - a);
        } else {
            return Math.abs(a - b);
        }
    }

    public static double distance(int a, int b) {
        if (a < b) {
            return Math.abs(b - a);
        } else {
            return Math.abs(a - b);
        }
    }

    private MathUtils() {
        throw constructorIllegal();
    }
}