MathUtils.java

/*
 * Copyright (C) 2016 The Android Open Source Project
 *
 * Licensed 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 androidx.core.math;

/**
 * A utility class providing functions useful for common mathematical operations.
 */
public class MathUtils {

    private MathUtils() {}

    /**
     * Returns the sum of its arguments, throwing an exception if the result overflows an
     * {@code int}.
     *
     * @param x the first value
     * @param y the second value
     * @return the result
     * @throws ArithmeticException if the result overflows an int
     */
    public static int addExact(int x, int y) {
        int sum = x + y;
        // If x and y have the same sign, their sum should have the same sign as well
        if ((x >= 0 == y >= 0) && (x >= 0 != sum >= 0)) {
            throw new ArithmeticException("integer overflow");
        } else {
            return sum;
        }
    }

    /**
     * Returns the sum of its arguments, throwing an exception if the result overflows a
     * {@code long}.
     *
     * @param x the first value
     * @param y the second value
     * @return the result
     * @throws ArithmeticException if the result overflows a long
     */
    public static long addExact(long x, long y) {
        long sum = x + y;
        // If x and y have the same sign, their sum should have the same sign as well
        if ((x >= 0 == y >= 0) && (x >= 0 != sum >= 0)) {
            throw new ArithmeticException("integer overflow");
        } else {
            return sum;
        }
    }

    /**
     * Returns the difference of the arguments, throwing an exception if the result overflows an
     * {@code int}.
     *
     * @param x the first value
     * @param y the second value to subtract from the first
     * @return the result
     * @throws ArithmeticException if the result overflows an int
     */
    public static int subtractExact(int x, int y) {
        int difference = x - y;
        // If only one of x or y is negative, the difference should have the same sign as x
        if ((x < 0 != y < 0) && (x < 0 != difference < 0)) {
            throw new ArithmeticException("integer overflow");
        }
        return difference;
    }

    /**
     * Returns the difference of the arguments, throwing an exception if the result overflows a
     * {@code long}.
     *
     * @param x the first value
     * @param y the second value to subtract from the first
     * @return the result
     * @throws ArithmeticException if the result overflows a long
     */
    public static long subtractExact(long x, long y) {
        long difference = x - y;
        // If only one of x or y is negative, the difference should have the same sign as x
        if ((x < 0 != y < 0) && (x < 0 != difference < 0)) {
            throw new ArithmeticException("integer overflow");
        }
        return difference;
    }

    /**
     * Returns the product of the arguments, throwing an exception if the result overflows an
     * {@code int}.
     *
     * @param x the first value
     * @param y the second value
     * @return the result
     * @throws ArithmeticException if the result overflows an int
     */
    public static int multiplyExact(int x, int y) {
        int product = x * y;
        // Dividing back by one of x or y should get the other back unless there was overflow
        if (x != 0 && y != 0 && (product / x != y || product / y != x)) {
            throw new ArithmeticException("integer overflow");
        }
        return product;
    }

    /**
     * Returns the product of the arguments, throwing an exception if the result overflows a
     * {@code long}.
     *
     * @param x the first value
     * @param y the second value
     * @return the result
     * @throws ArithmeticException if the result overflows a long
     */
    public static long multiplyExact(long x, long y) {
        long product = x * y;
        // Dividing back by one of x or y should get the other back unless there was overflow
        if (x != 0 && y != 0 && (product / x != y || product / y != x)) {
            throw new ArithmeticException("integer overflow");
        }
        return product;
    }

    /**
     * Returns the argument incremented by one, throwing an exception if the result overflows an
     * {@code int}. The overflow only occurs for {@linkplain Integer#MAX_VALUE the maximum value}.
     *
     * @param a the value to increment
     * @return the result
     * @throws ArithmeticException if the result overflows an int
     */
    public static int incrementExact(int a) {
        if (a == Integer.MAX_VALUE) {
            throw new ArithmeticException("integer overflow");
        } else {
            return a + 1;
        }
    }

    /**
     * Returns the argument incremented by one, throwing an exception if the result overflows a
     * {@code long}. The overflow only occurs for {@linkplain Long#MAX_VALUE the maximum value}.
     *
     * @param a the value to increment
     * @return the result
     * @throws ArithmeticException if the result overflows a long
     */
    public static long incrementExact(long a) {
        if (a == Long.MAX_VALUE) {
            throw new ArithmeticException("integer overflow");
        } else {
            return a + 1;
        }
    }

    /**
     * Returns the argument decremented by one, throwing an exception if the result overflows an
     * {@code int}. The overflow only occurs for {@linkplain Integer#MIN_VALUE the minimum value}.
     *
     * @param a the value to decrement
     * @return the result
     * @throws ArithmeticException if the result overflows an int
     */
    public static int decrementExact(int a) {
        if (a == Integer.MIN_VALUE) {
            throw new ArithmeticException("integer overflow");
        } else {
            return a - 1;
        }
    }

    /**
     * Returns the argument decremented by one, throwing an exception if the result overflows a
     * {@code long}. The overflow only occurs for {@linkplain Long#MIN_VALUE the minimum value}.
     *
     * @param a the value to decrement
     * @return the result
     * @throws ArithmeticException if the result overflows a long
     */
    public static long decrementExact(long a) {
        if (a == Long.MIN_VALUE) {
            throw new ArithmeticException("integer overflow");
        } else {
            return a - 1;
        }
    }

    /**
     * Returns the negation of the argument, throwing an exception if the result overflows an
     * {@code int}. The overflow only occurs for {@linkplain Integer#MIN_VALUE the minimum value}.
     *
     * @param a the value to negate
     * @return the result
     * @throws ArithmeticException if the result overflows an int
     */
    public static int negateExact(int a) {
        if (a == Integer.MIN_VALUE) {
            throw new ArithmeticException("integer overflow");
        } else {
            return -a;
        }
    }

    /**
     * Returns the negation of the argument, throwing an exception if the result overflows a
     * {@code long}. The overflow only occurs for {@linkplain Long#MIN_VALUE the minimum value}.
     *
     * @param a the value to negate
     * @return the result
     * @throws ArithmeticException if the result overflows a long
     */
    public static long negateExact(long a) {
        if (a == Long.MIN_VALUE) {
            throw new ArithmeticException("integer overflow");
        } else {
            return -a;
        }
    }

    /**
     * Returns the value of the {@code long} argument, throwing an exception if the value
     * overflows an {@code int}.
     *
     * @param value the long value
     * @return the argument as an int
     * @throws ArithmeticException if the {@code argument} overflows an int
     */
    public static int toIntExact(long value) {
        if (value > Integer.MAX_VALUE || value < Integer.MIN_VALUE) {
            throw new ArithmeticException("integer overflow");
        } else {
            return (int) value;
        }
    }

    /**
     * This method takes a numerical value and ensures it fits in a given numerical range. If the
     * number is smaller than the minimum required by the range, then the minimum of the range will
     * be returned. If the number is higher than the maximum allowed by the range then the maximum
     * of the range will be returned.
     *
     * @param value the value to be clamped.
     * @param min minimum resulting value.
     * @param max maximum resulting value.
     *
     * @return the clamped value.
     */
    public static float clamp(float value, float min, float max) {
        if (value < min) {
            return min;
        } else if (value > max) {
            return max;
        }
        return value;
    }

    /**
     * This method takes a numerical value and ensures it fits in a given numerical range. If the
     * number is smaller than the minimum required by the range, then the minimum of the range will
     * be returned. If the number is higher than the maximum allowed by the range then the maximum
     * of the range will be returned.
     *
     * @param value the value to be clamped.
     * @param min minimum resulting value.
     * @param max maximum resulting value.
     *
     * @return the clamped value.
     */
    public static double clamp(double value, double min, double max) {
        if (value < min) {
            return min;
        } else if (value > max) {
            return max;
        }
        return value;
    }

    /**
     * This method takes a numerical value and ensures it fits in a given numerical range. If the
     * number is smaller than the minimum required by the range, then the minimum of the range will
     * be returned. If the number is higher than the maximum allowed by the range then the maximum
     * of the range will be returned.
     *
     * @param value the value to be clamped.
     * @param min minimum resulting value.
     * @param max maximum resulting value.
     *
     * @return the clamped value.
     */
    public static int clamp(int value, int min, int max) {
        if (value < min) {
            return min;
        } else if (value > max) {
            return max;
        }
        return value;
    }

    /**
     * This method takes a numerical value and ensures it fits in a given numerical range. If the
     * number is smaller than the minimum required by the range, then the minimum of the range will
     * be returned. If the number is higher than the maximum allowed by the range then the maximum
     * of the range will be returned.
     *
     * @param value the value to be clamped.
     * @param min minimum resulting value.
     * @param max maximum resulting value.
     *
     * @return the clamped value.
     */
    public static long clamp(long value, long min, long max) {
        if (value < min) {
            return min;
        } else if (value > max) {
            return max;
        }
        return value;
    }
}