/*
 * @author Ranides Atterwim <ranides@gmail.com>
 * @copyright Ranides Atterwim
 * @license WTFPL
 * @url http://ranides.net/projects/assira
 */
package net.ranides.assira.math;

public final class MathUtils {

    private MathUtils() {
        // utility class
    }

    public static boolean between(int value, int min, int max) {
        return value >= min && value <= max;
    }

    public static boolean between(byte value, byte min, byte max) {
        return value >= min && value <= max;
    }

    public static boolean between(long value, long min, long max) {
        return value >= min && value <= max;
    }

    public static int clip(int value, int min, int max) {
        assert min <= max;
        return value < min ? min : value > max ? max : value;
    }
    
    public static long clip(long value, long min, long max) {
        assert min <= max;
        return value < min ? min : value > max ? max : value;
    }

    public static double clip(double value, double min, double max) {
        assert min <= max;
        return value < min ? min : value > max ? max : value;
    }

    public static float clip(float value, float min, float max) {
        assert min <= max;
        return value < min ? min : value > max ? max : value;
    }

    public static int round(double value, int min, int max) {
        assert min <= max;
        return (int)Math.round(value < min ? min : value > max ? max : value);
    }

    public static long round(double value, long min, long max) {
        assert min <= max;
        return Math.round(value < min ? min : value > max ? max : value);
    }
    
    public static int signum(int diff) {
        return Integer.signum(diff);
    }

    /**
     * Alternate to signum for use in compare. Not a true signum, since it
     * returns ints other than +/-1. Where there is any possibility of overflow,
     * you should compare two longs with < rather than subtraction.
     * <p>
     * In Pentium assembler you could implement this algorthm with following code:
     * <pre>
     *  # diff = edx:eax
     *  # result = eax
     *  mov ebx,eax
     *  shl eax,1
     *  or  eax,ebx
     *  slr eax,1
     *  or  eax,edx
     * </pre>
     * which would take 5 cycles, 2 more that lohi.  However, JET did even better,
     * with code essentially this using a clever trick to implement piotr.
     * <pre>
     *   lea    ecx,0(eax,eax)  ; shifts lo left by doubling, keeps copy of
     *   lo
     *   or     eax,ecx
     *   shr    eax,1
     *   or     eax,edx
     * </pre>
     * This is 4 cycles, still one more than lohi. Why was Piotr so much
     * faster on JET? Peter has no pipeline-confounding jumps. Further, the lo
     * then high operands actually come from the ram-based stack. Piotr nicely
     * separates the accesses giving plenty of for pre-emptive fetch of hi.
     * lohi insists on having them both upfront, so it has to wait for memory
     * access. Piotr does not have to wait. Modern CPUS hurry up and wait for
     * RAM most of the time.
     * </p>
     * @param diff number to be collapsed to an int preserving sign and
     * zeroness. usually represents the difference of two long.
     *
     * @return sign of {@code diff}, some {@code int < 0}, 0 or some {@code int > 0}.
     * @author Peter Kobzda
     */
    public static int signum(long diff) {
        return (int) (diff >>> 32) | ((int) diff | (int) diff << 1) >>> 1;
    }

    public static int log2floor(int x) {
        return 31 - Integer.numberOfLeadingZeros(x);
    }
    
    public static int log2floor(long x) {
        return 63 - Long.numberOfLeadingZeros(x);
    }

    public static int log2ceil(int x) {
        return 32 - Integer.numberOfLeadingZeros(x - 1);
    }
    
    public static int log2ceil(long x) {
        return 64 - Long.numberOfLeadingZeros(x - 1);
    }
    
    /**
     * Return the least power of two greater than or equal to the specified
     * value.
     *
     * <p>Note that this function will return 1 when the argument is 0.
     *
     * @param x an integer smaller than or equal to 2<sup>30</sup>.
     * @return the least power of two greater than or equal to the specified
     * value.
     */
    @SuppressWarnings({
        "PMD.AvoidReassigningParameters"
    })
    public static int pow2next(int x) {
        if (x == 0) {
            return 1;
        }
        x--;
        x |= x >> 1;
        x |= x >> 2;
        x |= x >> 4;
        x |= x >> 8;
        return (x | x >> 16) + 1;
    }

    /**
     * Return the least power of two greater than or equal to the specified
     * value.
     *
     * <p>Note that this function will return 1 when the argument is 0.
     *
     * @param x a long integer smaller than or equal to 2<sup>62</sup>.
     * @return the least power of two greater than or equal to the specified
     * value.
     */
    @SuppressWarnings({
        "PMD.AvoidReassigningParameters"
    })
    public static long pow2next(long x) {
        if (x == 0) {
            return 1;
        }
        x--;
        x |= x >> 1;
        x |= x >> 2;
        x |= x >> 4;
        x |= x >> 8;
        x |= x >> 16;
        return (x | x >> 32) + 1;
    }
    
    public static int gcd(int a, int b) { 
        return b==0 ? a : gcd(b, a % b); 
    }
    
    public static int gcd(int... values) {
        int ret = gcd(values[0], values[1]);
        for(int i=2; i<values.length; i++) {
            ret = gcd(ret, values[i]);
        }
        return ret;
    }
    
    public static long gcd(long a, long b) { 
        return b==0 ? a : gcd(b, a % b); 
    }
    
    public static long gcd(long... values) {
        long ret = gcd(values[0], values[1]);
        for(int i=2; i<values.length; i++) {
            ret = gcd(ret, values[i]);
        }
        return ret;
    }
    
    public static int lcm(int a, int b) {
        return (a / gcd(a,b)) * b;
    }
    
    public static int lcm(int... values) {
        int ret = lcm(values[0], values[1]);
        for(int i=2; i<values.length; i++) {
            ret = lcm(ret, values[i]);
        }
        return ret;
    }
    
    public static long lcm(long a, long b) {
        return (a/ gcd(a,b) ) * b;
    }
    
    public static long lcm(long... values) {
        long ret = lcm(values[0], values[1]);
        for(int i=2; i<values.length; i++) {
            ret = lcm(ret, values[i]);
        }
        return ret;
    }
}

