package org.math.plot.utils;

/*
 * Copyright 2012 Jeff Hain
 *
 * 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.
 */

/**
 * Class containing various basic utility methods to deal with numbers.
 * This class is meant to be light (no big look-up tables or such).
 * 
 * Check methods return boolean if success,
 * for it allows to use them in assertions.
 * 
 * toString methods use capital letters, unlike JDK's toStrings, for it is more
 * readable (especially, "l" and "1" can easily be confused with one another).
 * 
 * Some methods have an int version additionally to the long version,
 * even though long version could be used instead, for performance reasons,
 * either for the methods themselves (if they do computations with ints
 * instead of longs), or to be used in an int use case (like methods
 * checking whether or not a signed int can fit in such number of bits).
 */
public strictfp final class NumbersUtils {

    //--------------------------------------------------------------------------
    // CONFIGURATION
    //--------------------------------------------------------------------------

    private static final boolean ASSERTIONS = false;
    
    //--------------------------------------------------------------------------
    // MEMBERS
    //--------------------------------------------------------------------------

    private static final int MIN_DOUBLE_EXPONENT = -1074;
    private static final int MAX_DOUBLE_EXPONENT = 1023;

    /**
     * All possible upper case chars for representing a number as a String.
     */
    private final static char[] CHAR_BY_DIGIT;
    static {
        final char minDecimal = '0';
        final char maxDecimal = '9';
        final int n1 = maxDecimal - minDecimal + 1;
        final char minLetter = 'A';
        final char maxLetter = 'Z';
        final int n2 = maxLetter - minLetter + 1;
        CHAR_BY_DIGIT = new char[n1+n2];
        int i=0;
        for (char c=minDecimal;c<=maxDecimal;c++) {
            CHAR_BY_DIGIT[i++] = c;
        }
        for (char c=minLetter;c<=maxLetter;c++) {
            CHAR_BY_DIGIT[i++] = c;
        }
    }

    /**
     * For power-of-two radixes only.
     */
    private static final int[] DIV_SHIFT_BY_RADIX;
    static {
        DIV_SHIFT_BY_RADIX = new int[32+1];
        int shift=1;
        for (int radix=2;radix<=32;radix*=2) {
            DIV_SHIFT_BY_RADIX[radix] = shift++;
        }
    }

    private final static int[] MAX_NBR_OF_NEG_INT_DIGITS_BY_RADIX = new int[Character.MAX_RADIX+1];
    private final static int[] MAX_NBR_OF_NEG_LONG_DIGITS_BY_RADIX = new int[Character.MAX_RADIX+1];
    static {
        for (int radix=Character.MIN_RADIX;radix<=Character.MAX_RADIX;radix++) {
            /*
             * Brutal but works.
             * -1 for the sign.
             */
            MAX_NBR_OF_NEG_INT_DIGITS_BY_RADIX[radix] = Integer.toString(Integer.MIN_VALUE, radix).length()-1;
            MAX_NBR_OF_NEG_LONG_DIGITS_BY_RADIX[radix] = Long.toString(Long.MIN_VALUE, radix).length()-1;
        }
    }

    //--------------------------------------------------------------------------
    // PUBLIC METHODS
    //--------------------------------------------------------------------------

    /**
     * @return True if the specified values are equal or both NaN, false otherwise.
     */
    public static boolean equal(float a, float b) {
        // Only does one test if a == b.
        return (a == b) ? true : ((a != a) && (b != b));
    }

    /**
     * @return True if the specified values are equal or both NaN, false otherwise.
     */
    public static boolean equal(double a, double b) {
        // Only does one test if a == b.
        return (a == b) ? true : ((a != a) && (b != b));
    }

    /*
     * min/max ranges
     */

    /**
     * @return True if the specified value is in the specified range (inclusive), false otherwise.
     */
    public static boolean isInRange(int min, int max, int a) {
        return (min <= a) && (a <= max);
    }

    /**
     * @return True if the specified value is in the specified range (inclusive), false otherwise.
     */
    public static boolean isInRange(long min, long max, long a) {
        return (min <= a) && (a <= max);
    }

    /**
     * Returns false if any value is NaN.
     * 
     * @return True if the specified value is in the specified range (inclusive), false otherwise.
     */
    public static boolean isInRange(float min, float max, float a) {
        return (min <= a) && (a <= max);
    }

    /**
     * Returns false if any value is NaN.
     * 
     * @return True if the specified value is in the specified range (inclusive), false otherwise.
     */
    public static boolean isInRange(double min, double max, double a) {
        return (min <= a) && (a <= max);
    }

    /*
     * 
     */

    /**
     * @return True if does not throw.
     * @throws IllegalArgumentException if the specified value is not in the specified range (inclusive).
     */
    public static boolean checkIsInRange(int min, int max, int a) {
        if (!isInRange(min, max, a)) {
            throw new IllegalArgumentException(a+" not in ["+min+","+max+"]");
        }
        return true;
    }

    /**
     * @return True if does not throw.
     * @throws IllegalArgumentException if the specified value is not in the specified range (inclusive).
     */
    public static boolean checkIsInRange(long min, long max, long a) {
        if (!isInRange(min, max, a)) {
            throw new IllegalArgumentException(a+" not in ["+min+","+max+"]");
        }
        return true;
    }

    /**
     * @return True if does not throw.
     * @throws IllegalArgumentException if the specified value is not in the specified range (inclusive)
     *         or any parameter is NaN.
     */
    public static boolean checkIsInRange(float min, float max, float a) {
        if (!isInRange(min, max, a)) {
            throw new IllegalArgumentException(a+" not in ["+min+","+max+"]");
        }
        return true;
    }

    /**
     * @return True if does not throw.
     * @throws IllegalArgumentException if the specified value is not in the specified range (inclusive)
     *         or any parameter is NaN.
     */
    public static boolean checkIsInRange(double min, double max, double a) {
        if (!isInRange(min, max, a)) {
            throw new IllegalArgumentException(a+" not in ["+min+","+max+"]");
        }
        return true;
    }

    /*
     * bitwise ranges
     */

    /**
     * @param bitSize A number of bits, in [1,32].
     * @return True if the specified value fits as a signed integer
     *         over the specified number of bits, false otherwise.
     * @throws IllegalArgumentException if the specified number of bits is not in [1,32].
     */
    public static boolean isInRangeSigned(int a, int bitSize) {
        checkBitSizeForSignedInt(bitSize);
        return (minSignedIntForBitSize_noCheck(bitSize) <= a) && (a <= maxSignedIntForBitSize_noCheck(bitSize));
    }

    /**
     * @param bitSize A number of bits, in [1,64].
     * @return True if the specified value fits as a signed integer
     *         over the specified number of bits, false otherwise.
     * @throws IllegalArgumentException if the specified number of bits is not in [1,64].
     */
    public static boolean isInRangeSigned(long a, int bitSize) {
        checkBitSizeForSignedLong(bitSize);
        return (minSignedLongForBitSize_noCheck(bitSize) <= a) && (a <= maxSignedLongForBitSize_noCheck(bitSize));
    }

    /**
     * @param bitSize A number of bits, in [1,31].
     * @return True if the specified value fits as an unsigned integer
     *         over the specified number of bits, false otherwise.
     * @throws IllegalArgumentException if the specified number of bits is not in [1,31].
     */
    public static boolean isInRangeUnsigned(int a, int bitSize) {
        return isInRange(0, maxUnsignedIntForBitSize(bitSize), a);
    }

    /**
     * @param bitSize A number of bits, in [1,63].
     * @return True if the specified value fits as an unsigned integer
     *         over the specified number of bits, false otherwise.
     * @throws IllegalArgumentException if the specified number of bits is not in [1,63].
     */
    public static boolean isInRangeUnsigned(long a, int bitSize) {
        return isInRange(0, maxUnsignedLongForBitSize(bitSize), a);
    }

    /*
     * 
     */

    /**
     * @param bitSize A number of bits, in [1,32].
     * @return True if does not throw.
     * @throws IllegalArgumentException if the specified value does not fit
     *         as a signed integer over the specified number of bits.
     */
    public static boolean checkIsInRangeSigned(int a, int bitSize) {
        if (!isInRangeSigned(a, bitSize)) {
            throw new IllegalArgumentException(a+" does not fit as a signed value over "+bitSize+" bits");
        }
        return true;
    }

    /**
     * @param bitSize A number of bits, in [1,64].
     * @return True if does not throw.
     * @throws IllegalArgumentException if the specified value does not fit
     *         as a signed integer over the specified number of bits.
     */
    public static boolean checkIsInRangeSigned(long a, int bitSize) {
        if (!isInRangeSigned(a, bitSize)) {
            throw new IllegalArgumentException(a+" does not fit as a signed value over "+bitSize+" bits");
        }
        return true;
    }

    /**
     * @param bitSize A number of bits, in [1,31].
     * @return True if does not throw.
     * @throws IllegalArgumentException if the specified value does not fit
     *         as an unsigned integer over the specified number of bits.
     */
    public static boolean checkIsInRangeUnsigned(int a, int bitSize) {
        if (!isInRangeUnsigned(a, bitSize)) {
            throw new IllegalArgumentException(a+" does not fit as an unsigned value over "+bitSize+" bits");
        }
        return true;
    }

    /**
     * @param bitSize A number of bits, in [1,63].
     * @return True if does not throw.
     * @throws IllegalArgumentException if the specified value does not fit
     *         as an unsigned integer over the specified number of bits.
     */
    public static boolean checkIsInRangeUnsigned(long a, int bitSize) {
        if (!isInRangeUnsigned(a, bitSize)) {
            throw new IllegalArgumentException(a+" does not fit as an unsigned value over "+bitSize+" bits");
        }
        return true;
    }

    /*
     * masks (int)
     */

    /**
     * @param bitSize A number of bits, in [0,32].
     * @return Mask with the specified number of left bits set with 0,
     *         and other bits set with 1.
     */
    public static int intMaskMSBits0(int bitSize) {
        checkIsInRange(0, 32, bitSize);
        // Shifting in two times, for >>> doesn't work for full bit size (<< as well).
        final int halfish = (bitSize>>1);
        return ((-1)>>>halfish)>>>(bitSize-halfish);
    }

    /**
     * @param bitSize A number of bits, in [0,32].
     * @return Mask with the specified number of left bits set with 1,
     *         and other bits set with 0.
     */
    public static int intMaskMSBits1(int bitSize) {
        return ~intMaskMSBits0(bitSize);
    }

    /**
     * @param bitSize A number of bits, in [0,32].
     * @return Mask with the specified number of right bits set with 0,
     *         and other bits set with 1.
     */
    public static int intMaskLSBits0(int bitSize) {
        return ~intMaskMSBits0(32-bitSize);
    }

    /**
     * @param bitSize A number of bits, in [0,32].
     * @return Mask with the specified number of right bits set with 1,
     *         and other bits set with 0.
     */
    public static int intMaskLSBits1(int bitSize) {
        return intMaskMSBits0(32-bitSize);
    }

    /*
     * masks (long)
     */

    /**
     * @param bitSize A number of bits, in [0,64].
     * @return Mask with the specified number of left bits set with 0,
     *         and other bits set with 1.
     */
    public static long longMaskMSBits0(int bitSize) {
        checkIsInRange(0, 64, bitSize);
        // Shifting in two times, for >>> doesn't work for full bit size (<< as well).
        final int halfish = (bitSize>>1);
        return ((-1L)>>>halfish)>>>(bitSize-halfish);
    }

    /**
     * @param bitSize A number of bits, in [0,64].
     * @return Mask with the specified number of left bits set with 1,
     *         and other bits set with 0.
     */
    public static long longMaskMSBits1(int bitSize) {
        return ~longMaskMSBits0(bitSize);
    }

    /**
     * @param bitSize A number of bits, in [0,64].
     * @return Mask with the specified number of right bits set with 0,
     *         and other bits set with 1.
     */
    public static long longMaskLSBits0(int bitSize) {
        return ~longMaskMSBits0(64-bitSize);
    }

    /**
     * @param bitSize A number of bits, in [0,64].
     * @return Mask with the specified number of right bits set with 1,
     *         and other bits set with 0.
     */
    public static long longMaskLSBits1(int bitSize) {
        return longMaskMSBits0(64-bitSize);
    }

    /*
     * signed/unsigned
     */

    /**
     * @return Unsigned value corresponding to bits of the specified byte.
     */
    public static short byteAsUnsigned(byte value) {
        return (short)(((short)value) & 0xFF);
    }

    /**
     * @return Unsigned value corresponding to bits of the specified short.
     */
    public static int shortAsUnsigned(short value) {
        return ((int)value) & 0xFFFF;
    }

    /**
     * @return Unsigned value corresponding to bits of the specified int.
     */
    public static long intAsUnsigned(int value) {
        return ((long)value) & 0xFFFFFFFF;
    }

    /*
     * bitwise ranges
     */

    /**
     * @return True if a signed int value can be read over the specified number of bits,
     *         i.e. if it is in [1,32], false otherwise.
     */
    public static boolean isValidBitSizeForSignedInt(int bitSize) {
        return (bitSize > 0) && (bitSize <= 32);
    }

    /**
     * @return True if a signed long value can be read over the specified number of bits,
     *         i.e. if it is in [1,64], false otherwise.
     */
    public static boolean isValidBitSizeForSignedLong(int bitSize) {
        return (bitSize > 0) && (bitSize <= 64);
    }

    /**
     * @return True if an unsigned int value can be read over the specified number of bits,
     *         i.e. if it is in [1,31], false otherwise.
     */
    public static boolean isValidBitSizeForUnsignedInt(int bitSize) {
        return (bitSize > 0) && (bitSize < 32);
    }

    /**
     * @return True if an unsigned long value can be read over the specified number of bits,
     *         i.e. if it is in [1,63], false otherwise.
     */
    public static boolean isValidBitSizeForUnsignedLong(int bitSize) {
        return (bitSize > 0) && (bitSize < 64);
    }

    /*
     * 
     */

    /**
     * @return True if does not throw.
     * @throws IllegalArgumentException if a signed int value can't be read over the
     *         specified number of bits, i.e. if it is not in [1,32].
     */
    public static boolean checkBitSizeForSignedInt(int bitSize) {
        if (!isValidBitSizeForSignedInt(bitSize)) {
            throw new IllegalArgumentException("bit size ["+bitSize+"] must be in [1,32] for signed int values");
        }
        return true;
    }

    /**
     * @return True if does not throw.
     * @throws IllegalArgumentException if a signed long value can't be read over the
     *         specified number of bits, i.e. if it is not in [1,64].
     */
    public static boolean checkBitSizeForSignedLong(int bitSize) {
        if (!isValidBitSizeForSignedLong(bitSize)) {
            throw new IllegalArgumentException("bit size ["+bitSize+"] must be in [1,64] for signed long values");
        }
        return true;
    }

    /**
     * @return True if does not throw.
     * @throws IllegalArgumentException if an unsigned int value can't be read over the
     *         specified number of bits, i.e. if it is not in [1,31].
     */
    public static boolean checkBitSizeForUnsignedInt(int bitSize) {
        if (!isValidBitSizeForUnsignedInt(bitSize)) {
            throw new IllegalArgumentException("bit size ["+bitSize+"] must be in [1,31] for unsigned int values");
        }
        return true;
    }

    /**
     * @return True if does not throw.
     * @throws IllegalArgumentException if an unsigned long value can't be read over the
     *         specified number of bits, i.e. if it is not in [1,63].
     */
    public static boolean checkBitSizeForUnsignedLong(int bitSize) {
        if (!isValidBitSizeForUnsignedLong(bitSize)) {
            throw new IllegalArgumentException("bit size ["+bitSize+"] must be in [1,63] for unsigned long values");
        }
        return true;
    }

    /*
     * 
     */

    /**
     * @param bitSize A number of bits in [1,32].
     * @return The min signed int value that can be stored over the specified number of bits.
     * @throws IllegalArgumentException if the specified number of bits is out of range.
     */
    public static int minSignedIntForBitSize(int bitSize) {
        checkBitSizeForSignedInt(bitSize);
        return minSignedIntForBitSize_noCheck(bitSize);
    }

    /**
     * @param bitSize A number of bits in [1,64].
     * @return The min signed long value that can be stored over the specified number of bits.
     * @throws IllegalArgumentException if the specified number of bits is out of range.
     */
    public static long minSignedLongForBitSize(int bitSize) {
        checkBitSizeForSignedLong(bitSize);
        return minSignedLongForBitSize_noCheck(bitSize);
    }

    /**
     * @param bitSize A number of bits in [1,32].
     * @return The max signed int value that can be stored over the specified number of bits.
     * @throws IllegalArgumentException if the specified number of bits is out of range.
     */
    public static int maxSignedIntForBitSize(int bitSize) {
        checkBitSizeForSignedInt(bitSize);
        return maxSignedIntForBitSize_noCheck(bitSize);
    }

    /**
     * @param bitSize A number of bits in [1,64].
     * @return The max signed long value that can be stored over the specified number of bits.
     * @throws IllegalArgumentException if the specified number of bits is out of range.
     */
    public static long maxSignedLongForBitSize(int bitSize) {
        checkBitSizeForSignedLong(bitSize);
        return maxSignedLongForBitSize_noCheck(bitSize);
    }

    /**
     * @param bitSize A number of bits in [1,31].
     * @return The max unsigned int value that can be stored over the specified number of bits.
     * @throws IllegalArgumentException if the specified number of bits is out of range.
     */
    public static int maxUnsignedIntForBitSize(int bitSize) {
        checkBitSizeForUnsignedInt(bitSize);
        // i.e. (1<<bitSize)-1
        return (Integer.MAX_VALUE>>(31-bitSize));
    }

    /**
     * @param bitSize A number of bits in [1,63].
     * @return The max unsigned long value that can be stored over the specified number of bits.
     * @throws IllegalArgumentException if the specified number of bits is out of range.
     */
    public static long maxUnsignedLongForBitSize(int bitSize) {
        checkBitSizeForUnsignedLong(bitSize);
        // i.e. (1L<<bitSize)-1
        return (Long.MAX_VALUE>>(63-bitSize));
    }

    /*
     * 
     */

    /**
     * @return The number of bits required to store the specified value as a signed integer,
     *         i.e. a result in [1,32].
     */
    public static int bitSizeForSignedValue(int value) {
        if (value > 0) {
            return 33-Integer.numberOfLeadingZeros(value);
        } else if (value == 0) {
            return 1;
        } else {
            // Works for Integer.MIN_VALUE as well.
            return 33-Integer.numberOfLeadingZeros(-value-1);
        }
    }

    /**
     * @return The number of bits required to store the specified value as a signed integer,
     *         i.e. a result in [1,64].
     */
    public static int bitSizeForSignedValue(long value) {
        if (value > 0) {
            return 65-Long.numberOfLeadingZeros(value);
        } else if (value == 0) {
            return 1;
        } else {
            // Works for Long.MIN_VALUE as well.
            return 65-Long.numberOfLeadingZeros(-value-1);
        }
    }

    /**
     * @param value An integer value in [0,Integer.MAX_VALUE].
     * @return The number of bits required to store the specified value as an unsigned integer,
     *         i.e. a result in [1,31].
     * @throws IllegalArgumentException if the specified value is &lt; 0.
     */
    public static int bitSizeForUnsignedValue(int value) {
        if (value > 0) {
            return 32-Integer.numberOfLeadingZeros(value);
        } else {
            if (value == 0) {
                return 1;
            } else {
                throw new IllegalArgumentException("unsigned value ["+value+"] must be >= 0");
            }
        }
    }

    /**
     * @param value An integer value in [0,Long.MAX_VALUE].
     * @return The number of bits required to store the specified value as an unsigned integer,
     *         i.e. a result in [1,63].
     * @throws IllegalArgumentException if the specified value is &lt; 0.
     */
    public static int bitSizeForUnsignedValue(long value) {
        if (value > 0) {
            return 64-Long.numberOfLeadingZeros(value);
        } else {
            if (value == 0) {
                return 1;
            } else {
                throw new IllegalArgumentException("unsigned value ["+value+"] must be >= 0");
            }
        }
    }

    /*
     * integer functions
     */
    
    /**
     * @return 1 if the specified value is &gt; 0, 0 if it is 0, -1 otherwise.
     */
    public static int signum(int a) {
        return (a < 0) ? -1 : ((a == 0) ? 0 : 1);
    }

    /**
     * @return 1 if the specified value is &gt; 0, 0 if it is 0, -1 otherwise.
     */
    public static int signum(long a) {
        return (a < 0) ? -1 : ((a == 0) ? 0 : 1);
    }

    /**
     * @return True if the specified value is even, false otherwise.
     */
    public static boolean isEven(int a) {
        return ((a&1) == 0);
    }

    /**
     * @return True if the specified value is even, false otherwise.
     */
    public static boolean isEven(long a) {
        // faster to work on ints
        return isEven((int)a);
    }

    /**
     * @return True if the specified value is odd, false otherwise.
     */
    public static boolean isOdd(int a) {
        return ((a&1) != 0);
    }

    /**
     * @return True if the specified value is odd, false otherwise.
     */
    public static boolean isOdd(long a) {
        // faster to work on ints
        return isOdd((int)a);
    }

    /**
     * @return True if the specified values are both even or both odd, false otherwise.
     */
    public static boolean haveSameEvenness(int a, int b) {
        return (((a^b)&1) == 0);
    }

    /**
     * @return True if the specified values are both even or both odd, false otherwise.
     */
    public static boolean haveSameEvenness(long a, long b) {
        // faster to work on ints
        return haveSameEvenness((int)a, (int)b);
    }

    /**
     * @return True if the specified values are both &gt;= 0 or both &lt; 0, false otherwise.
     */
    public static boolean haveSameSign(int a, int b) {
        return ((a^b) >= 0);
    }

    /**
     * @return True if the specified values are both &gt;= 0 or both &lt; 0, false otherwise.
     */
    public static boolean haveSameSign(long a, long b) {
        return ((a^b) >= 0);
    }

    /**
     * @return True if the specified value is a power of two,
     *         i.e. a value of the form 2^k, with k &gt;= 0.
     */
    public static boolean isPowerOfTwo(int a) {
        if (a <= 0) {
            return false;
        }
        if (false) {
            // also works
            return (a & -a) == a;
        }
        return (a & (a-1)) == 0;
    }

    /**
     * @return True if the specified value is a power of two,
     *         i.e. a value of the form 2^k, with k &gt;= 0.
     */
    public static boolean isPowerOfTwo(long a) {
        if (a <= 0) {
            return false;
        }
        if (false) {
            // also works
            return (a & -a) == a;
        }
        return (a & (a-1)) == 0;
    }

    /**
     * @return True if the specified value is a signed power of two,
     *         i.e. a value of the form +-2^k, with k &gt;= 0.
     */
    public static boolean isSignedPowerOfTwo(int a) {
        if (a > 0) {
            return (a & (a-1)) == 0;
        } else {
            if (a == -a) {
                // a is 0 or Integer.MIN_VALUE
                return (a != 0);
            }
            return ((-a) & (-a-1)) == 0;
        }
    }

    /**
     * @return True if the specified value is a signed power of two,
     *         i.e. a value of the form +-2^k, with k &gt;= 0.
     */
    public static boolean isSignedPowerOfTwo(long a) {
        if (a > 0) {
            return (a & (a-1)) == 0;
        } else {
            if (a == -a) {
                // a is 0 or Long.MIN_VALUE
                return (a != 0);
            }
            return ((-a) & (-a-1)) == 0;
        }
    }

    /**
     * @param a A value in [1,Integer.MAX_VALUE].
     * @return The highest power of two &lt;= a.
     */
    public static int floorPowerOfTwo(int a) {
        if (a <= 0) {
            throw new IllegalArgumentException("a ["+a+"] must be > 0");
        }
        return 1 << (31 - Integer.numberOfLeadingZeros(a));
    }

    /**
     * @param a A value in [1,Long.MAX_VALUE].
     * @return The highest power of two &lt;= a.
     */
    public static long floorPowerOfTwo(long a) {
        if (a <= 0) {
            throw new IllegalArgumentException("a ["+a+"] must be > 0");
        }
        return 1L << (63 - Long.numberOfLeadingZeros(a));
    }

    /**
     * @param a A value in [0,2^30].
     * @return The lowest power of two &gt;= a.
     */
    public static int ceilingPowerOfTwo(int a) {
        checkIsInRange(0, (1<<30), a);
        // From Hacker's Delight, Chapter 3, Harry S. Warren Jr.
        return 1 << (32 - Integer.numberOfLeadingZeros(a - 1));
    }

    /**
     * @param a A value in [0,2^62].
     * @return The lowest power of two &gt;= a.
     */
    public static long ceilingPowerOfTwo(long a) {
        checkIsInRange(0L, (1L<<62), a);
        return 1L << (64 - Long.numberOfLeadingZeros(a - 1));
    }

    /**
     * @return Mean without overflow, rounded to the lowest value (i.e. mathematical floor((a+b)/2), using floating point division).
     */
    public static int meanLow(int a, int b) {
        return (a & b) + ((a ^ b) >> 1);
    }

    /**
     * @return Mean without overflow, rounded to the lowest value (i.e. mathematical floor((a+b)/2), using floating point division).
     */
    public static long meanLow(long a, long b) {
        return (a & b) + ((a ^ b) >> 1);
    }

    /**
     * @return Mean without overflow, rounded to the value of smallest magnitude (i.e. mathematical (a+b)/2, using integer division).
     */
    public static int meanSml(int a, int b) {
        int result = meanLow(a,b);
        if (!haveSameEvenness(a, b)) {
            // inexact
            if (((a&b) < 0) || (((a|b) < 0) && (a+b < 0))) {
                // both &lt; 0, or only one is &lt; 0 and it has the largest magnitude
                result++;
            }
        }
        return result;
    }

    /**
     * @return Mean without overflow, rounded to the value of smallest magnitude (i.e. mathematical (a+b)/2, using integer division).
     */
    public static long meanSml(long a, long b) {
        long result = meanLow(a,b);
        if (!haveSameEvenness(a, b)) {
            // inexact
            if (((a&b) < 0) || (((a|b) < 0) && (a+b < 0))) {
                // both &lt; 0, or only one is &lt; 0 and it has the largest magnitude
                result++;
            }
        }
        return result;
    }

    /**
     * Useful because a positive int value could not represent half the width
     * of full int range width, which is mathematically Integer.MAX_VALUE+1.
     * 
     * @return Minus half the range width (inclusive, and rounded to the value of smaller magnitude)
     *         between the specified bounds.
     * @throws IllegalArgumentException if min &gt; max.
     */
    public static int negHalfWidth(int min, int max) {
        if (min > max) {
            throw new IllegalArgumentException("min ["+min+"] must be <= max ["+max+"]");
        }
        int mean = meanLow(min, max);
        return min - mean - ((min^max)&1);
    }

    /**
     * Useful because a positive long value could not represent half the width
     * of full long range width, which is mathematically Long.MAX_VALUE+1.
     * 
     * @return Minus half the range width (inclusive, and rounded to the value of smaller magnitude)
     *         between the specified bounds.
     * @throws IllegalArgumentException if min &gt; max.
     */
    public static long negHalfWidth(long min, long max) {
        if (min > max) {
            throw new IllegalArgumentException("min ["+min+"] must be <= max ["+max+"]");
        }
        long mean = meanLow(min, max);
        return min - mean - ((min^max)&1);
    }

    /**
     * This treatment being designed for optimization, the fact that spot
     * is a signed power of two is only checked if assertions are enabled.
     * 
     * @param value A value.
     * @param spot A signed power of two (i.e. a value of the form +-2^k, k &gt;= 0).
     * @return value % spot, i.e. a value in ]-|spot|,|spot|[.
     */
    public static int moduloSignedPowerOfTwo(int value, int spot) {
        if(ASSERTIONS)assert(isSignedPowerOfTwo(spot));
        if (spot == Integer.MIN_VALUE) {
            return (value != Integer.MIN_VALUE) ? value : 0;
        } else {
            int s = (value>>31);
            return ((((value+s) ^ s) & (abs(spot)-1)) + s) ^ s;
        }
    }

    /**
     * This treatment being designed for optimization, the fact that spot
     * is a signed power of two is only checked if assertions are enabled.
     * 
     * @param value A value.
     * @param spot A signed power of two (i.e. a value of the form +-2^k, k &gt;= 0).
     * @return value % spot, i.e. a value in ]-|spot|,|spot|[.
     */
    public static long moduloSignedPowerOfTwo(long value, long spot) {
        if(ASSERTIONS)assert(isSignedPowerOfTwo(spot));
        if (spot == Long.MIN_VALUE) {
            return (value != Long.MIN_VALUE) ? value : 0;
        } else {
            long s = (value>>63);
            return ((((value+s) ^ s) & (abs(spot)-1)) + s) ^ s;
        }
    }

    /**
     * @param value An integer value &gt; 0.
     * @return The integer part of the logarithm, in base 2, of the specified value,
     *         i.e. a result in [0,30]
     * @throws IllegalArgumentException if the specified value is &lt;= 0.
     */
    public static int log2(int value) {
        if (value <= 0) {
            throw new IllegalArgumentException("value ["+value+"] must be > 0");
        }
        return 31-Integer.numberOfLeadingZeros(value);
    }

    /**
     * @param value An integer value &gt; 0.
     * @return The integer part of the logarithm, in base 2, of the specified value,
     *         i.e. a result in [0,62]
     * @throws IllegalArgumentException if the specified value is &lt;= 0.
     */
    public static int log2(long value) {
        if (value <= 0) {
            throw new IllegalArgumentException("value ["+value+"] must be > 0");
        }
        return 63-Long.numberOfLeadingZeros(value);
    }

    /**
     * Possibly faster than java.lang.Math.abs(int).
     * 
     * @return The absolute value, except if value is Integer.MIN_VALUE, for which it returns Integer.MIN_VALUE.
     */
    public static int abs(int a) {
        return (a^(a>>31))-(a>>31);
    }

    /**
     * Possibly faster than java.lang.Math.abs(long).
     * 
     * @return The absolute value, except if value is Long.MIN_VALUE, for which it returns Long.MIN_VALUE.
     */
    public static long abs(long a) {
        return (a^(a>>63))-(a>>63);
    }

    /**
     * net.jodk.FastMath class has a typically faster version of this method,
     * using look-up tables.
     * 
     * Returns the exact result, provided it's in double range,
     * i.e. if power is in [-1074,1023].
     * 
     * @param power A power.
     * @return 2^power.
     */
    public static double twoPow(int power) {
        if (power <= -MAX_DOUBLE_EXPONENT) { // Not normal.
            if (power >= MIN_DOUBLE_EXPONENT) { // Subnormal.
                return Double.longBitsToDouble(0x0008000000000000L>>(-(power+MAX_DOUBLE_EXPONENT)));
            } else { // Underflow.
                return 0.0;
            }
        } else if (power > MAX_DOUBLE_EXPONENT) { // Overflow.
            return Double.POSITIVE_INFINITY;
        } else { // Normal.
            return Double.longBitsToDouble(((long)(power+MAX_DOUBLE_EXPONENT))<<52);
        }
    }

    /**
     * If the specified value is in int range, the returned value is identical.
     * 
     * @return An int hash of the specified value.
     */
    public static int intHash(long a) {
        if (false) {
            // also works
            int hash = ((int)(a>>32)) ^ ((int)a);
            if (a < 0) {
                hash = -hash-1;
            }
            return hash;
        }
        int hash = ((int)(a>>32)) + ((int)a);
        if (a < 0) {
            hash++;
        }
        return hash;
    }

    /**
     * Not defining an asByte(long) method, since asByte((int)aLong) works.
     * 
     * @param a An int value.
     * @return The specified value as byte.
     * @throws ArithmeticException if the specified value is not in [Byte.MIN_VALUE,Byte.MAX_VALUE] range.
     */
    public static byte asByte(int a) {
        if (a != (byte)a) {
            throw new ArithmeticException("overflow: "+a);
        }
        return (byte)a;
    }

    /**
     * @param a A long value.
     * @return The specified value as int.
     * @throws ArithmeticException if the specified value is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range.
     */
    public static int asInt(long a) {
        if (a != (int)a) {
            throw new ArithmeticException("overflow: "+a);
        }
        return (int)a;
    }

    /**
     * @param a A long value.
     * @return The closest int value in [Integer.MIN_VALUE,Integer.MAX_VALUE] range.
     */
    public static int toInt(long a) {
        if (a != (int)a) {
            return (a < (long)Integer.MIN_VALUE) ? Integer.MIN_VALUE : Integer.MAX_VALUE;
        }
        return (int)a;
    }

    /**
     * @param a An int value.
     * @param b An int value.
     * @return The mathematical result of a+b.
     * @throws ArithmeticException if the mathematical result of a+b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range.
     */
    public static int plusExact(int a, int b) {
        if ((a^b) < 0) { // test if a and b signs are different
            return a + b;
        } else {
            int sum = a + b;
            if ((a^sum) < 0) {
                throw new ArithmeticException("overflow: "+a+"+"+b);
            } else {
                return sum;
            }
        }
    }

    /**
     * @param a A long value.
     * @param b A long value.
     * @return The mathematical result of a+b.
     * @throws ArithmeticException if the mathematical result of a+b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range.
     */
    public static long plusExact(long a, long b) {
        if ((a^b) < 0) { // test if a and b signs are different
            return a + b;
        } else {
            long sum = a + b;
            if ((a^sum) < 0) {
                throw new ArithmeticException("overflow: "+a+"+"+b);
            } else {
                return sum;
            }
        }
    }

    /**
     * @param a An int value.
     * @param b An int value.
     * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a+b.
     */
    public static int plusBounded(int a, int b) {
        return toInt(((long)a) + ((long)b));
    }

    /**
     * @param a A long value.
     * @param b A long value.
     * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a+b.
     */
    public static long plusBounded(long a, long b) {
        if ((a^b) < 0) { // test if a and b signs are different
            return a + b;
        } else {
            long sum = a + b;
            if ((a^sum) < 0) {
                return (sum >= 0) ? Long.MIN_VALUE : Long.MAX_VALUE;
            } else {
                return sum;
            }
        }
    }

    /**
     * @param a An int value.
     * @param b An int value.
     * @return The mathematical result of a-b.
     * @throws ArithmeticException if the mathematical result of a-b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range.
     */
    public static int minusExact(int a, int b) {
        if ((a^b) >= 0) { // test if a and b signs are identical
            return a - b;
        } else {
            int diff = a - b;
            if ((a^diff) < 0) {
                throw new ArithmeticException("overflow: "+a+"-"+b);
            } else {
                return diff;
            }
        }
    }

    /**
     * @param a A long value.
     * @param b A long value.
     * @return The mathematical result of a-b.
     * @throws ArithmeticException if the mathematical result of a-b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range.
     */
    public static long minusExact(long a, long b) {
        if ((a^b) >= 0) { // test if a and b signs are identical
            return a - b;
        } else {
            long diff = a - b;
            if ((a^diff) < 0) {
                throw new ArithmeticException("overflow: "+a+"-"+b);
            } else {
                return diff;
            }
        }
    }

    /**
     * @param a An int value.
     * @param b An int value.
     * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a-b.
     */
    public static int minusBounded(int a, int b) {
        return toInt(((long)a) - ((long)b));
    }

    /**
     * @param a A long value.
     * @param b A long value.
     * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a-b.
     */
    public static long minusBounded(long a, long b) {
        if ((a^b) >= 0) { // test if a and b signs are identical
            return a - b;
        } else {
            long diff = a - b;
            if ((a^diff) < 0) {
                return (diff >= 0) ? Long.MIN_VALUE : Long.MAX_VALUE;
            } else {
                return diff;
            }
        }
    }

    /**
     * @param a An int value.
     * @param b An int value.
     * @return The mathematical result of a*b.
     * @throws ArithmeticException if the mathematical result of a*b is not in [Integer.MIN_VALUE,Integer.MAX_VALUE] range.
     */
    public static int timesExact(int a, int b) {
        long product = a * (long)b;
        if (product != (int)product) {
            throw new ArithmeticException("overflow: "+a+"*"+b);
        }
        return (int)product;
    }

    /**
     * @param a A long value.
     * @param b A long value.
     * @return The mathematical result of a*b.
     * @throws ArithmeticException if the mathematical result of a*b is not in [Long.MIN_VALUE,Long.MAX_VALUE] range.
     */
    public static long timesExact(long a, long b) {
        long r = a * b;
        long absA = abs(a);
        long absB = abs(b);
        if (((absA|absB)>>>31) != 0) {
            // Some bits greater than 2^31 that might cause overflow
            // Check the result using the divide operator
            // and check for the special case of Long.MIN_VALUE * -1
            if (((b != 0) && (r/b != a)) ||
                    ((a == Long.MIN_VALUE) && (b == -1))) {
                throw new ArithmeticException("overflow: "+a+"*"+b);
            }
        }
        return r;
    }

    /**
     * @param a An int value.
     * @param b An int value.
     * @return The int value of [Integer.MIN_VALUE,Integer.MAX_VALUE] range which is the closest to mathematical result of a*b.
     */
    public static int timesBounded(int a, int b) {
        return (int)(a * (double)b);
    }

    /**
     * @param a A long value.
     * @param b A long value.
     * @return The long value of [Long.MIN_VALUE,Long.MAX_VALUE] range which is the closest to mathematical result of a*b.
     */
    public static long timesBounded(long a, long b) {
        long r = a * b;
        long absA = abs(a);
        long absB = abs(b);
        if (((absA|absB)>>>31) != 0) {
            // Some bits greater than 2^31 that might cause overflow
            // Check the result using the divide operator
            // and check for the special case of Long.MIN_VALUE * -1
            if (((b != 0) && (r/b != a)) ||
                    ((a == Long.MIN_VALUE) && (b == -1))) {
                return ((a^b) >= 0) ? Long.MAX_VALUE : Long.MIN_VALUE;
            }
        }
        return r;
    }

    /*
     * integer and floating point functions
     */

    /**
     * @return True if the specified value is NaN, positive of negative infinity, false otherwise.
     */
    public static boolean isNaNOrInfinite(float a) {
        // a-a is not equal to 0.0f (and is NaN) <-> a is NaN or +-infinity
        return !(a-a == 0.0f);
    }

    /**
     * @return True if the specified value is NaN, positive of negative infinity, false otherwise.
     */
    public static boolean isNaNOrInfinite(double a) {
        // a-a is not equal to 0.0 (and is NaN) <-> a is NaN or +-infinity
        return !(a-a == 0.0);
    }

    /**
     * @param a A value.
     * @return a*a.
     */
    public static int pow2(int a) {
        return a*a;
    }

    /**
     * @param a A value.
     * @return a*a.
     */
    public static long pow2(long a) {
        return a*a;
    }

    /**
     * @param a A value.
     * @return a*a.
     */
    public static float pow2(float a) {
        return a*a;
    }

    /**
     * @param a A value.
     * @return a*a.
     */
    public static double pow2(double a) {
        return a*a;
    }

    /**
     * @param a A value.
     * @return a*a*a.
     */
    public static int pow3(int a) {
        return a*a*a;
    }

    /**
     * @param a A value.
     * @return a*a*a.
     */
    public static long pow3(long a) {
        return a*a*a;
    }

    /**
     * @param a A value.
     * @return a*a*a.
     */
    public static float pow3(float a) {
        return a*a*a;
    }

    /**
     * @param a A value.
     * @return a*a*a.
     */
    public static double pow3(double a) {
        return a*a*a;
    }

    /*
     * 
     */

    /**
     * @param min A value.
     * @param max A value.
     * @param a A value.
     * @return min if a &lt;= min, else max if a &gt;= max, else a.
     */
    public static int toRange(int min, int max, int a) {
        if (a <= min) {
            return min;
        } else if (a >= max) {
            return max;
        } else {
            return a;
        }
    }

    /**
     * @param min A value.
     * @param max A value.
     * @param a A value.
     * @return min if a &lt;= min, else max if a &gt;= max, else a.
     */
    public static long toRange(long min, long max, long a) {
        if (a <= min) {
            return min;
        } else if (a >= max) {
            return max;
        } else {
            return a;
        }
    }

    /**
     * @param min A value.
     * @param max A value.
     * @param a A value.
     * @return min if a &lt;= min, else max if a &gt;= max, else a.
     */
    public static float toRange(float min, float max, float a) {
        if (a <= min) {
            return min;
        } else if (a >= max) {
            return max;
        } else {
            return a;
        }
    }

    /**
     * @param min A value.
     * @param max A value.
     * @param a A value.
     * @return min if a &lt;= min, else max if a &gt;= max, else a.
     */
    public static double toRange(double min, double max, double a) {
        if (a <= min) {
            return min;
        } else if (a >= max) {
            return max;
        } else {
            return a;
        }
    }

    /*
     * toString (radix)
     */

    /**
     * @param radix Radix to be checked.
     * @return True if does not throw.
     * @throws IllegalArgumentException if the specified radix is not in [2,36].
     */
    public static boolean checkRadix(int radix) {
        if (!isInRange(Character.MIN_RADIX, Character.MAX_RADIX, radix)) {
            throw new IllegalArgumentException("radix ["+radix+"] must be in ["+Character.MIN_RADIX+","+Character.MAX_RADIX+"]");
        }
        return true;
    }

    /**
     * @param radix A radix in [2,36].
     * @return Number of characters (minus sign included)
     *         to represent the specified value in the specified radix.
     */
    public static int computeNbrOfChars(int value, int radix) {
        if (value < 0) {
            // 1 for sign
            return 1 + computeNbrOfDigits_negValue(value, radix);
        } else {
            return computeNbrOfDigits_negValue(-value, radix);
        }
    }

    /**
     * @param radix A radix in [2,36].
     * @return Number of characters (minus sign included)
     *         to represent the specified value in the specified radix.
     */
    public static int computeNbrOfChars(long value, int radix) {
        if (value < 0) {
            // 1 for sign
            return 1 + computeNbrOfDigits_negValue(value, radix);
        } else {
            return computeNbrOfDigits_negValue(-value, radix);
        }
    }

    /**
     * @param radix A radix in [2,36].
     * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done.
     * @return Number of characters (minus sign included)
     *         to represent the specified value in the specified radix.
     */
    public static int computeNbrOfChars(int value, int radix, int paddingUpTo) {
        if (value < 0) {
            // 1 for sign
            return 1 + Math.max(paddingUpTo, computeNbrOfDigits_negValue(value, radix));
        } else {
            return Math.max(paddingUpTo, computeNbrOfDigits_negValue(-value, radix));
        }
    }

    /**
     * @param radix A radix in [2,36].
     * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done.
     * @return Number of characters (minus sign included)
     *         to represent the specified value in the specified radix.
     */
    public static int computeNbrOfChars(long value, int radix, int paddingUpTo) {
        if (value < 0) {
            // 1 for sign
            return 1 + Math.max(paddingUpTo, computeNbrOfDigits_negValue(value, radix));
        } else {
            return Math.max(paddingUpTo, computeNbrOfDigits_negValue(-value, radix));
        }
    }

    /**
     * @param radix A radix in [2,36].
     * @return Number of digits of the specified value in the specified radix.
     */
    public static int computeNbrOfDigits(int value, int radix) {
        return computeNbrOfDigits_negValue(-abs(value), radix);
    }

    /**
     * @param radix A radix in [2,36].
     * @return Number of digits of the specified value in the specified radix.
     */
    public static int computeNbrOfDigits(long value, int radix) {
        return computeNbrOfDigits_negValue(-abs(value), radix);
    }

    /**
     * @param radix A radix in [2,36].
     * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done.
     * @return Number of digits of the specified value in the specified radix,
     *         including the specified padding.
     */
    public static int computeNbrOfDigits(int value, int radix, int paddingUpTo) {
        return Math.max(paddingUpTo,computeNbrOfDigits(value, radix));
    }

    /**
     * @param radix A radix in [2,36].
     * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done.
     * @return Number of digits of the specified value in the specified radix,
     *         including the specified padding.
     */
    public static int computeNbrOfDigits(long value, int radix, int paddingUpTo) {
        return Math.max(paddingUpTo,computeNbrOfDigits(value, radix));
    }

    /**
     * This method just delegates to Integer.toString(int),
     * but is defined here to complete the API.
     * 
     * @return String representation of the specified value in base 10.
     */
    public static String toString(int value) {
        return Integer.toString(value);
    }

    /**
     * This method just delegates to Long.toString(long),
     * but is defined here to complete the API.
     * 
     * @return String representation of the specified value in base 10.
     */
    public static String toString(long value) {
        return Long.toString(value);
    }

    /**
     * @param radix A radix in [2,36].
     * @return String representation of the specified value in the specified radix.
     * @throws IllegalArgumentException if the specified radix is out of range.
     */
    public static String toString(int value, int radix) {
        return toString(value, radix, 0);
    }

    /**
     * @param radix A radix in [2,36].
     * @return String representation of the specified value in the specified radix.
     * @throws IllegalArgumentException if the specified radix is out of range.
     */
    public static String toString(long value, int radix) {
        return toString(value, radix, 0);
    }

    /**
     * @param radix A radix in [2,36].
     * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done.
     * @return String representation of the specified value in the specified radix.
     * @throws IllegalArgumentException if the specified radix is out of range.
     */
    public static String toString(int value, int radix, int paddingUpTo) {
        // Only one test if radix+paddingUpTo != 10.
        if ((radix+paddingUpTo == 10) && (paddingUpTo == 0)) {
            // Using JDK's optimized algorithm.
            return Integer.toString(value);
        }
        
        int negValue;
        final int signSize;
        final boolean negative = (value < 0);
        if (negative) {
            negValue = value;
            signSize = 1;
        } else {
            negValue = -value;
            signSize = 0;
        }
        // Faster if we just use max possible number of characters (33),
        // but we prefer to take care of garbage's memory footprint.
        // Checks radix.
        final int nbrOfChars = signSize + Math.max(paddingUpTo, computeNbrOfDigits_negValue(negValue, radix));

        final char[] chars = new char[nbrOfChars];

        int charPos = nbrOfChars;

        final boolean radixIsPowerOfTwo = ((radix & (radix-1)) == 0);
        // Not allowing Integer.MIN_VALUE so it can be negated.
        if (radixIsPowerOfTwo && (negValue != Integer.MIN_VALUE)) {
            final int mask = radix-1;
            final int divShift = DIV_SHIFT_BY_RADIX[radix];
            while (negValue <= -radix) {
                chars[--charPos] = CHAR_BY_DIGIT[(int)((-negValue) & mask)];
                negValue = -((-negValue) >> divShift);
            }
        } else {
            while (negValue <= -radix) {
                chars[--charPos] = CHAR_BY_DIGIT[(int)(-(negValue % radix))];
                negValue /= radix;
            }
        }
        chars[--charPos] = CHAR_BY_DIGIT[(int)(-negValue)];

        while (charPos > signSize) {
            chars[--charPos] = '0';
        }

        if (negative) {
            chars[0] = '-';
        }

        return new String(chars);
    }

    /**
     * @param radix A radix in [2,36].
     * @param paddingUpTo Number of digits (sign excluded) up to which left-padding with zeros is done.
     * @return String representation of the specified value in the specified radix.
     * @throws IllegalArgumentException if the specified radix is out of range.
     */
    public static String toString(long value, int radix, int paddingUpTo) {
        // Only one test if radix+paddingUpTo != 10.
        if ((radix+paddingUpTo == 10) && (paddingUpTo == 0)) {
            // Using JDK's optimized algorithm.
            return Long.toString(value);
        }
        
        long negValue;
        final int signSize;
        final boolean negative = (value < 0);
        if (negative) {
            negValue = value;
            signSize = 1;
        } else {
            negValue = -value;
            signSize = 0;
        }
        // Checks radix.
        final int nbrOfChars = signSize + Math.max(paddingUpTo, computeNbrOfDigits_negValue(negValue, radix));

        final char[] chars = new char[nbrOfChars];

        int charPos = nbrOfChars;

        final boolean radixIsPowerOfTwo = ((radix & (radix-1)) == 0);
        // Not allowing Long.MIN_VALUE so it can be negated.
        if (radixIsPowerOfTwo && (negValue != Long.MIN_VALUE)) {
            final int mask = radix-1;
            final int divShift = DIV_SHIFT_BY_RADIX[radix];
            while (negValue <= -radix) {
                chars[--charPos] = CHAR_BY_DIGIT[(int)((-negValue) & mask)];
                negValue = -((-negValue) >> divShift);
            }
        } else {
            while (negValue <= -radix) {
                chars[--charPos] = CHAR_BY_DIGIT[(int)(-(negValue % radix))];
                negValue /= radix;
            }
        }
        chars[--charPos] = CHAR_BY_DIGIT[(int)(-negValue)];

        while (charPos > signSize) {
            chars[--charPos] = '0';
        }

        if (negative) {
            chars[0] = '-';
        }

        return new String(chars);
    }

    /*
     * toString (bits)
     */

    /**
     * @param firstBitPos First bit position (inclusive).
     * @param lastBitPosExcl Last bit position (exclusive).
     * @return True if does not throw.
     * @throws IllegalArgumentException if the specified bit range does not fit in a byte.
     */
    public static boolean checkBitPositionsByte(int firstBitPos, int lastBitPosExcl) {
        return checkBitPositions(firstBitPos, lastBitPosExcl, 8);
    }

    /**
     * @param firstBitPos First bit position (inclusive).
     * @param lastBitPosExcl Last bit position (exclusive).
     * @return True if does not throw.
     * @throws IllegalArgumentException if the specified bit range does not fit in a short.
     */
    public static boolean checkBitPositionsShort(int firstBitPos, int lastBitPosExcl) {
        return checkBitPositions(firstBitPos, lastBitPosExcl, 16);
    }

    /**
     * @param firstBitPos First bit position (inclusive).
     * @param lastBitPosExcl Last bit position (exclusive).
     * @return True if does not throw.
     * @throws IllegalArgumentException if the specified bit range does not fit in an int.
     */
    public static boolean checkBitPositionsInt(int firstBitPos, int lastBitPosExcl) {
        return checkBitPositions(firstBitPos, lastBitPosExcl, 32);
    }

    /**
     * @param firstBitPos First bit position (inclusive).
     * @param lastBitPosExcl Last bit position (exclusive).
     * @return True if does not throw.
     * @throws IllegalArgumentException if the specified bit range does not fit in a long.
     */
    public static boolean checkBitPositionsLong(int firstBitPos, int lastBitPosExcl) {
        return checkBitPositions(firstBitPos, lastBitPosExcl, 64);
    }

    /**
     * @return String representation of specified bits, in big endian.
     */
    public static String toStringBits(byte bits) {
        final char[] chars = new char[8];
        int bitIndex = 8;
        while (--bitIndex >= 0) {
            chars[7-bitIndex] = (char)('0'+((bits>>bitIndex)&1));
        }
        return new String(chars);
    }

    /**
     * @return String representation of specified bits, in big endian.
     */
    public static String toStringBits(short bits) {
        final char[] chars = new char[16];
        int bitIndex = 16;
        while (--bitIndex >= 0) {
            chars[15-bitIndex] = (char)('0'+((bits>>bitIndex)&1));
        }
        return new String(chars);
    }

    /**
     * @return String representation of specified bits, in big endian.
     */
    public static String toStringBits(int bits) {
        final char[] chars = new char[32];
        int bitIndex = 32;
        while (--bitIndex >= 0) {
            chars[31-bitIndex] = (char)('0'+((bits>>bitIndex)&1));
        }
        return new String(chars);
    }

    /**
     * @return String representation of specified bits, in big endian.
     */
    public static String toStringBits(long bits) {
        final char[] chars = new char[64];
        int bitIndex = 64;
        while (--bitIndex >= 0) {
            chars[63-bitIndex] = (char)('0'+((bits>>bitIndex)&1));
        }
        return new String(chars);
    }

    /**
     * @param firstBitPos First bit position (inclusive).
     * @param lastBitPosExcl Last bit position (exclusive).
     * @param bigEndian True for bits to be added in big endian order (MSBit to LSBit)
     *                  false for little endian order.
     * @param padding True if underscores must be added instead of out-of-range bits,
     *                false to just add characters corresponding to in-range bits.
     * @return String representation of specified bits.
     */
    public static String toStringBits(
            byte bits,
            int firstBitPos,
            int lastBitPosExcl,
            boolean bigEndian,
            boolean padding) {
        checkBitPositionsByte(firstBitPos, lastBitPosExcl);
        return toStringBits_0_32_bitPosAlreadyChecked(8,bits, firstBitPos, lastBitPosExcl, bigEndian, padding);
    }

    /**
     * @param firstBitPos First bit position (inclusive).
     * @param lastBitPosExcl Last bit position (exclusive).
     * @param bigEndian True for bits to be added in big endian order (MSBit to LSBit)
     *                  false for little endian order.
     * @param padding True if underscores must be added instead of out-of-range bits,
     *                false to just add characters corresponding to in-range bits.
     * @return String representation of specified bits.
     */
    public static String toStringBits(
            short bits,
            int firstBitPos,
            int lastBitPosExcl,
            boolean bigEndian,
            boolean padding) {
        checkBitPositionsShort(firstBitPos, lastBitPosExcl);
        return toStringBits_0_32_bitPosAlreadyChecked(16,bits, firstBitPos, lastBitPosExcl, bigEndian, padding);
    }

    /**
     * @param firstBitPos First bit position (inclusive).
     * @param lastBitPosExcl Last bit position (exclusive).
     * @param bigEndian True for bits to be added in big endian order (MSBit to LSBit)
     *                  false for little endian order.
     * @param padding True if underscores must be added instead of out-of-range bits,
     *                false to just add characters corresponding to in-range bits.
     * @return String representation of specified bits.
     */
    public static String toStringBits(
            int bits,
            int firstBitPos,
            int lastBitPosExcl,
            boolean bigEndian,
            boolean padding) {
        checkBitPositionsInt(firstBitPos, lastBitPosExcl);
        return toStringBits_0_32_bitPosAlreadyChecked(32,bits, firstBitPos, lastBitPosExcl, bigEndian, padding);
    }

    /**
     * @param firstBitPos First bit position (inclusive).
     * @param lastBitPosExcl Last bit position (exclusive).
     * @param bigEndian True for bits to be added in big endian order (MSBit to LSBit)
     *                  false for little endian order.
     * @param padding True if underscores must be added instead of out-of-range bits,
     *                false to just add characters corresponding to in-range bits.
     * @return String representation of specified bits.
     */
    public static String toStringBits(
            long bits,
            int firstBitPos,
            int lastBitPosExcl,
            boolean bigEndian,
            boolean padding) {
        checkBitPositionsLong(firstBitPos, lastBitPosExcl);
        final int bitSize = 64;
        final int bitSizeM1 = bitSize-1;
        final int lastBitPos = lastBitPosExcl-1;
        if (padding) {
            final int nbrOfChars = bitSize;
            final char[] chars = new char[nbrOfChars];
            int bitIndex = bitSizeM1;
            if (bigEndian) {
                final int firstBitIndex = bitSizeM1-lastBitPos;
                final int lastBitIndex = bitSizeM1-firstBitPos;
                while (bitIndex > lastBitIndex) {
                    chars[bitSizeM1-bitIndex] = '_';
                    --bitIndex;
                }
                while (bitIndex >= firstBitIndex) {
                    chars[bitSizeM1-bitIndex] = (char)('0'+((bits>>bitIndex)&1));
                    --bitIndex;
                }
                while (bitIndex >= 0) {
                    chars[bitSizeM1-bitIndex] = '_';
                    --bitIndex;
                }
            } else {
                while (bitIndex > lastBitPos) {
                    chars[bitIndex] = '_';
                    --bitIndex;
                }
                while (bitIndex >= firstBitPos) {
                    chars[bitIndex] = (char)('0'+((bits>>bitIndex)&1));
                    --bitIndex;
                }
                while (bitIndex >= 0) {
                    chars[bitIndex] = '_';
                    --bitIndex;
                }
            }
            return new String(chars);
        } else {
            final int nbrOfChars = (lastBitPosExcl - firstBitPos);
            final char[] chars = new char[nbrOfChars];
            if (bigEndian) {
                final int firstBitIndex = bitSizeM1-lastBitPos;
                final int lastBitIndex = bitSizeM1-firstBitPos;
                int bitIndex = lastBitIndex;
                while (bitIndex >= firstBitIndex) {
                    chars[lastBitIndex-bitIndex] = (char)('0'+((bits>>bitIndex)&1));
                    --bitIndex;
                }
            } else {
                int bitIndex = lastBitPos;
                while (bitIndex >= firstBitPos) {
                    chars[bitIndex-firstBitPos] = (char)('0'+((bits>>bitIndex)&1));
                    --bitIndex;
                }
            }
            return new String(chars);
        }
    }

    //--------------------------------------------------------------------------
    // PRIVATE METHODS
    //--------------------------------------------------------------------------

    /**
     * Had such isInXXX methods, and corresponding checkXXX methods,
     * but they seem actually slower in practice, so just keeping this
     * code here in case some day it becomes faster than regular isInXXX.
     * 
     * Only works for non-empty ranges, i.e. such as min <= max.
     * This treatment being designed for optimization, min <= max
     * is only checked is assertions are enabled.
     * 
     * @return True if the specified value is in the specified range (inclusive), false otherwise.
     */
    private static boolean dontUseMe_isInNonEmptyRange_(int min, int max, int a) {
        if(ASSERTIONS)assert(min <= max);
        // Using modulo arithmetic.
        return (Integer.MIN_VALUE+(a-min) <= Integer.MIN_VALUE+(max-min));
    }

    /*
     * 
     */
    
    private static int minSignedIntForBitSize_noCheck(int bitSize) {
        // i.e. (-1<<(bitSize-1))
        return (Integer.MIN_VALUE>>(32-bitSize));
    }

    private static long minSignedLongForBitSize_noCheck(int bitSize) {
        // i.e. (-1L<<(bitSize-1))
        return (Long.MIN_VALUE>>(64-bitSize));
    }

    private static int maxSignedIntForBitSize_noCheck(int bitSize) {
        // i.e. (1<<(bitSize-1))-1
        return (Integer.MAX_VALUE>>(32-bitSize));
    }

    private static long maxSignedLongForBitSize_noCheck(int bitSize) {
        // i.e. (1L<<(bitSize-1))-1
        return (Long.MAX_VALUE>>(64-bitSize));
    }

    /*
     * 
     */

    /**
     * @throws IllegalArgumentException if the specified radix is out of range.
     */
    private static int computeNbrOfDigits_negValue(int negValue, int radix) {
        checkRadix(radix);
        final int maxNbrOfDigits = MAX_NBR_OF_NEG_INT_DIGITS_BY_RADIX[radix];
        int p = radix;
        for (int i=1;i<maxNbrOfDigits;i++) {
            if (negValue > -p) {
                return i;
            }
            p *= radix;
        }
        return maxNbrOfDigits;
    }

    /**
     * @throws IllegalArgumentException if the specified radix is out of range.
     */
    private static int computeNbrOfDigits_negValue(long negValue, int radix) {
        checkRadix(radix);
        final int maxNbrOfDigits = MAX_NBR_OF_NEG_LONG_DIGITS_BY_RADIX[radix];
        long p = radix;
        for (int i=1;i<maxNbrOfDigits;i++) {
            if (negValue > -p) {
                return i;
            }
            p *= radix;
        }
        return maxNbrOfDigits;
    }

    /*
     * 
     */

    private static boolean checkBitPositions(int firstBitPos, int lastBitPosExcl, int bitSize) {
        if ((firstBitPos < 0) || (firstBitPos > lastBitPosExcl) || (lastBitPosExcl > bitSize)) {
            throw new IllegalArgumentException(
                    "bit positions (first="+firstBitPos+",lastExcl="+lastBitPosExcl
                    +") must verify 0 <= first <= lastExcl <= "+bitSize);
        }
        return true;
    }

    /**
     * Common method for byte, short and int.
     * Could be a bit faster to have specific methods for byte and short,
     * but not much, and that would also make more messy (byte-)code.
     * 
     * @param bitSize Must be in [0,32].
     */
    private static String toStringBits_0_32_bitPosAlreadyChecked(
            int bitSize,
            int bits,
            int firstBitPos,
            int lastBitPosExcl,
            boolean bigEndian,
            boolean padding) {
        if(ASSERTIONS)assert((bitSize >= 0) && (bitSize <= 32));
        final int bitSizeM1 = bitSize-1;
        final int lastBitPos = lastBitPosExcl-1;
        if (padding) {
            final int nbrOfChars = bitSize;
            final char[] chars = new char[nbrOfChars];
            int bitIndex = bitSizeM1;
            if (bigEndian) {
                final int firstBitIndex = bitSizeM1-lastBitPos;
                final int lastBitIndex = bitSizeM1-firstBitPos;
                while (bitIndex > lastBitIndex) {
                    chars[bitSizeM1-bitIndex] = '_';
                    --bitIndex;
                }
                while (bitIndex >= firstBitIndex) {
                    chars[bitSizeM1-bitIndex] = (char)('0'+((bits>>bitIndex)&1));
                    --bitIndex;
                }
                while (bitIndex >= 0) {
                    chars[bitSizeM1-bitIndex] = '_';
                    --bitIndex;
                }
            } else {
                while (bitIndex > lastBitPos) {
                    chars[bitIndex] = '_';
                    --bitIndex;
                }
                while (bitIndex >= firstBitPos) {
                    chars[bitIndex] = (char)('0'+((bits>>bitIndex)&1));
                    --bitIndex;
                }
                while (bitIndex >= 0) {
                    chars[bitIndex] = '_';
                    --bitIndex;
                }
            }
            return new String(chars);
        } else {
            final int nbrOfChars = (lastBitPosExcl - firstBitPos);
            final char[] chars = new char[nbrOfChars];
            if (bigEndian) {
                final int firstBitIndex = bitSizeM1-lastBitPos;
                final int lastBitIndex = bitSizeM1-firstBitPos;
                int bitIndex = lastBitIndex;
                while (bitIndex >= firstBitIndex) {
                    chars[lastBitIndex-bitIndex] = (char)('0'+((bits>>bitIndex)&1));
                    --bitIndex;
                }
            } else {
                int bitIndex = lastBitPos;
                while (bitIndex >= firstBitPos) {
                    chars[bitIndex-firstBitPos] = (char)('0'+((bits>>bitIndex)&1));
                    --bitIndex;
                }
            }
            return new String(chars);
        }
    }
}