/*----------------------------------------------------------------------------*
 * This file is part of Pitaya.                                               *
 * Copyright (C) 2012-2016 Osman KOCAK <kocakosm@gmail.com>                   *
 *                                                                            *
 * This program is free software: you can redistribute it and/or modify it    *
 * under the terms of the GNU Lesser General Public License as published by   *
 * the Free Software Foundation, either version 3 of the License, or (at your *
 * option) any later version.                                                 *
 * This program 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 Lesser General Public     *
 * License for more details.                                                  *
 * You should have received a copy of the GNU Lesser General Public License   *
 * along with this program. If not, see <http://www.gnu.org/licenses/>.       *
 *----------------------------------------------------------------------------*/

package org.kocakosm.pitaya.util;

import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Random;

/**
 * Various extra utilities for arrays.
 *
 * @see Arrays
 *
 * @author Osman KOCAK
 */
public final class XArrays
{
	private static final Random PRNG = new Random();

	/**
	 * Returns whether the given array is {@code null} or {@code empty},
	 * that is, returns {@code true} if {@code a} is {@code null} or empty
	 * and {@code false} otherwise.
	 *
	 * @param a the array to test.
	 *
	 * @return whether the given array is {@code null} or {@code empty}.
	 */
	public static boolean isNullOrEmpty(long[] a)
	{
		return a == null || a.length == 0;
	}

	/**
	 * Returns whether the given array is {@code null} or {@code empty},
	 * that is, returns {@code true} if {@code a} is {@code null} or empty
	 * and {@code false} otherwise.
	 *
	 * @param a the array to test.
	 *
	 * @return whether the given array is {@code null} or {@code empty}.
	 */
	public static boolean isNullOrEmpty(int[] a)
	{
		return a == null || a.length == 0;
	}

	/**
	 * Returns whether the given array is {@code null} or {@code empty},
	 * that is, returns {@code true} if {@code a} is {@code null} or empty
	 * and {@code false} otherwise.
	 *
	 * @param a the array to test.
	 *
	 * @return whether the given array is {@code null} or {@code empty}.
	 */
	public static boolean isNullOrEmpty(short[] a)
	{
		return a == null || a.length == 0;
	}

	/**
	 * Returns whether the given array is {@code null} or {@code empty},
	 * that is, returns {@code true} if {@code a} is {@code null} or empty
	 * and {@code false} otherwise.
	 *
	 * @param a the array to test.
	 *
	 * @return whether the given array is {@code null} or {@code empty}.
	 */
	public static boolean isNullOrEmpty(char[] a)
	{
		return a == null || a.length == 0;
	}

	/**
	 * Returns whether the given array is {@code null} or {@code empty},
	 * that is, returns {@code true} if {@code a} is {@code null} or empty
	 * and {@code false} otherwise.
	 *
	 * @param a the array to test.
	 *
	 * @return whether the given array is {@code null} or {@code empty}.
	 */
	public static boolean isNullOrEmpty(byte[] a)
	{
		return a == null || a.length == 0;
	}

	/**
	 * Returns whether the given array is {@code null} or {@code empty},
	 * that is, returns {@code true} if {@code a} is {@code null} or empty
	 * and {@code false} otherwise.
	 *
	 * @param a the array to test.
	 *
	 * @return whether the given array is {@code null} or {@code empty}.
	 */
	public static boolean isNullOrEmpty(boolean[] a)
	{
		return a == null || a.length == 0;
	}

	/**
	 * Returns whether the given array is {@code null} or {@code empty},
	 * that is, returns {@code true} if {@code a} is {@code null} or empty
	 * and {@code false} otherwise.
	 *
	 * @param a the array to test.
	 *
	 * @return whether the given array is {@code null} or {@code empty}.
	 */
	public static boolean isNullOrEmpty(float[] a)
	{
		return a == null || a.length == 0;
	}

	/**
	 * Returns whether the given array is {@code null} or {@code empty},
	 * that is, returns {@code true} if {@code a} is {@code null} or empty
	 * and {@code false} otherwise.
	 *
	 * @param a the array to test.
	 *
	 * @return whether the given array is {@code null} or {@code empty}.
	 */
	public static boolean isNullOrEmpty(double[] a)
	{
		return a == null || a.length == 0;
	}

	/**
	 * Returns whether the given array is {@code null} or {@code empty},
	 * that is, returns {@code true} if {@code a} is {@code null} or empty
	 * and {@code false} otherwise.
	 *
	 * @param a the array to test.
	 *
	 * @return whether the given array is {@code null} or {@code empty}.
	 */
	public static boolean isNullOrEmpty(Object[] a)
	{
		return a == null || a.length == 0;
	}

	/**
	 * Concatenates the given arrays into a single one.
	 *
	 * @param arrays the arrays to concatenate.
	 *
	 * @return the concatenated array.
	 *
	 * @throws NullPointerException if {@code arrays} is {@code null} or if
	 *	it contains a {@code null} reference.
	 */
	public static long[] concat(long[]... arrays)
	{
		int len = 0;
		for (long[] array : arrays) {
			len += array.length;
		}
		long[] concat = new long[len];
		int i = 0;
		for (long[] array : arrays) {
			System.arraycopy(array, 0, concat, i, array.length);
			i = array.length;
		}
		return concat;
	}

	/**
	 * Concatenates the given arrays into a single one.
	 *
	 * @param arrays the arrays to concatenate.
	 *
	 * @return the concatenated array.
	 *
	 * @throws NullPointerException if {@code arrays} is {@code null} or if
	 *	it contains a {@code null} reference.
	 */
	public static int[] concat(int[]... arrays)
	{
		int len = 0;
		for (int[] array : arrays) {
			len += array.length;
		}
		int[] concat = new int[len];
		int i = 0;
		for (int[] array : arrays) {
			System.arraycopy(array, 0, concat, i, array.length);
			i = array.length;
		}
		return concat;
	}

	/**
	 * Concatenates the given arrays into a single one.
	 *
	 * @param arrays the arrays to concatenate.
	 *
	 * @return the concatenated array.
	 *
	 * @throws NullPointerException if {@code arrays} is {@code null} or if
	 *	it contains a {@code null} reference.
	 */
	public static short[] concat(short[]... arrays)
	{
		int len = 0;
		for (short[] array : arrays) {
			len += array.length;
		}
		short[] concat = new short[len];
		int i = 0;
		for (short[] array : arrays) {
			System.arraycopy(array, 0, concat, i, array.length);
			i = array.length;
		}
		return concat;
	}

	/**
	 * Concatenates the given arrays into a single one.
	 *
	 * @param arrays the arrays to concatenate.
	 *
	 * @return the concatenated array.
	 *
	 * @throws NullPointerException if {@code arrays} is {@code null} or if
	 *	it contains a {@code null} reference.
	 */
	public static char[] concat(char[]... arrays)
	{
		int len = 0;
		for (char[] array : arrays) {
			len += array.length;
		}
		char[] concat = new char[len];
		int i = 0;
		for (char[] array : arrays) {
			System.arraycopy(array, 0, concat, i, array.length);
			i = array.length;
		}
		return concat;
	}

	/**
	 * Concatenates the given arrays into a single one.
	 *
	 * @param arrays the arrays to concatenate.
	 *
	 * @return the concatenated array.
	 *
	 * @throws NullPointerException if {@code arrays} is {@code null} or if
	 *	it contains a {@code null} reference.
	 */
	public static byte[] concat(byte[]... arrays)
	{
		int len = 0;
		for (byte[] array : arrays) {
			len += array.length;
		}
		byte[] concat = new byte[len];
		int i = 0;
		for (byte[] array : arrays) {
			System.arraycopy(array, 0, concat, i, array.length);
			i = array.length;
		}
		return concat;
	}

	/**
	 * Concatenates the given arrays into a single one.
	 *
	 * @param arrays the arrays to concatenate.
	 *
	 * @return the concatenated array.
	 *
	 * @throws NullPointerException if {@code arrays} is {@code null} or if
	 *	it contains a {@code null} reference.
	 */
	public static boolean[] concat(boolean[]... arrays)
	{
		int len = 0;
		for (boolean[] array : arrays) {
			len += array.length;
		}
		boolean[] concat = new boolean[len];
		int i = 0;
		for (boolean[] array : arrays) {
			System.arraycopy(array, 0, concat, i, array.length);
			i = array.length;
		}
		return concat;
	}

	/**
	 * Concatenates the given arrays into a single one.
	 *
	 * @param arrays the arrays to concatenate.
	 *
	 * @return the concatenated array.
	 *
	 * @throws NullPointerException if {@code arrays} is {@code null} or if
	 *	it contains a {@code null} reference.
	 */
	public static float[] concat(float[]... arrays)
	{
		int len = 0;
		for (float[] array : arrays) {
			len += array.length;
		}
		float[] concat = new float[len];
		int i = 0;
		for (float[] array : arrays) {
			System.arraycopy(array, 0, concat, i, array.length);
			i = array.length;
		}
		return concat;
	}

	/**
	 * Concatenates the given arrays into a single one.
	 *
	 * @param arrays the arrays to concatenate.
	 *
	 * @return the concatenated array.
	 *
	 * @throws NullPointerException if {@code arrays} is {@code null} or if
	 *	it contains a {@code null} reference.
	 */
	public static double[] concat(double[]... arrays)
	{
		int len = 0;
		for (double[] array : arrays) {
			len += array.length;
		}
		double[] concat = new double[len];
		int i = 0;
		for (double[] array : arrays) {
			System.arraycopy(array, 0, concat, i, array.length);
			i = array.length;
		}
		return concat;
	}

	/**
	 * Concatenates the given arrays into a single one.
	 *
	 * @param <T> the type of the elements in the arrays to concatenate.
	 * @param arrays the arrays to concatenate.
	 *
	 * @return the concatenated array.
	 *
	 * @throws IllegalArgumentException if {@code arrays} is empty.
	 * @throws NullPointerException if {@code arrays} is {@code null} or if
	 *	it contains a {@code null} reference.
	 */
	public static <T> T[] concat(T[]... arrays)
	{
		Parameters.checkCondition(arrays.length > 0);
		int len = 0;
		for (T[] array : arrays) {
			len += array.length;
		}
		T[] concat = newArray(arrays[0].getClass().getComponentType(), len);
		int i = 0;
		for (T[] array : arrays) {
			System.arraycopy(array, 0, concat, i, array.length);
			i = array.length;
		}
		return concat;
	}

	/**
	 * Copies the specified range of elements from the given array into a
	 * new array. The returned array will be padded with {@code 0}, if
	 * necessary, so that it has an exact length of {@code len}.
	 *
	 * @param a the source array.
	 * @param off the starting position in the source array.
	 * @param len the number of elements to copy (returned array's length).
	 *
	 * @return a new array containing the specified range of the source array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 * @throws ArrayIndexOutOfBoundsException if {@code off} is negative.
	 * @throws NegativeArraySizeException if {@code len} is negative.
	 */
	public static long[] copyOf(long[] a, int off, int len)
	{
		long[] copy = new long[len];
		if (len > a.length - off) {
			System.arraycopy(a, off, copy, 0, a.length - off);
		} else {
			System.arraycopy(a, off, copy, 0, len);
		}
		return copy;
	}

	/**
	 * Copies the specified range of elements from the given array into a
	 * new array. The returned array will be padded with {@code 0}, if
	 * necessary, so that it has an exact length of {@code len}.
	 *
	 * @param a the source array.
	 * @param off the starting position in the source array.
	 * @param len the number of elements to copy (returned array's length).
	 *
	 * @return a new array containing the specified range of the source array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 * @throws ArrayIndexOutOfBoundsException if {@code off} is negative.
	 * @throws NegativeArraySizeException if {@code len} is negative.
	 */
	public static int[] copyOf(int[] a, int off, int len)
	{
		int[] copy = new int[len];
		if (len > a.length - off) {
			System.arraycopy(a, off, copy, 0, a.length - off);
		} else {
			System.arraycopy(a, off, copy, 0, len);
		}
		return copy;
	}

	/**
	 * Copies the specified range of elements from the given array into a
	 * new array. The returned array will be padded with {@code 0}, if
	 * necessary, so that it has an exact length of {@code len}.
	 *
	 * @param a the source array.
	 * @param off the starting position in the source array.
	 * @param len the number of elements to copy (returned array's length).
	 *
	 * @return a new array containing the specified range of the source array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 * @throws ArrayIndexOutOfBoundsException if {@code off} is negative.
	 * @throws NegativeArraySizeException if {@code len} is negative.
	 */
	public static short[] copyOf(short[] a, int off, int len)
	{
		short[] copy = new short[len];
		if (len > a.length - off) {
			System.arraycopy(a, off, copy, 0, a.length - off);
		} else {
			System.arraycopy(a, off, copy, 0, len);
		}
		return copy;
	}

	/**
	 * Copies the specified range of elements from the given array into a
	 * new array. The returned array will be padded with {@code 0}, if
	 * necessary, so that it has an exact length of {@code len}.
	 *
	 * @param a the source array.
	 * @param off the starting position in the source array.
	 * @param len the number of elements to copy (returned array's length).
	 *
	 * @return a new array containing the specified range of the source array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 * @throws ArrayIndexOutOfBoundsException if {@code off} is negative.
	 * @throws NegativeArraySizeException if {@code len} is negative.
	 */
	public static char[] copyOf(char[] a, int off, int len)
	{
		char[] copy = new char[len];
		if (len > a.length - off) {
			System.arraycopy(a, off, copy, 0, a.length - off);
		} else {
			System.arraycopy(a, off, copy, 0, len);
		}
		return copy;
	}

	/**
	 * Copies the specified range of elements from the given array into a
	 * new array. The returned array will be padded with {@code 0}, if
	 * necessary, so that it has an exact length of {@code len}.
	 *
	 * @param a the source array.
	 * @param off the starting position in the source array.
	 * @param len the number of elements to copy (returned array's length).
	 *
	 * @return a new array containing the specified range of the source array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 * @throws ArrayIndexOutOfBoundsException if {@code off} is negative.
	 * @throws NegativeArraySizeException if {@code len} is negative.
	 */
	public static byte[] copyOf(byte[] a, int off, int len)
	{
		byte[] copy = new byte[len];
		if (len > a.length - off) {
			System.arraycopy(a, off, copy, 0, a.length - off);
		} else {
			System.arraycopy(a, off, copy, 0, len);
		}
		return copy;
	}

	/**
	 * Copies the specified range of elements from the given array into a
	 * new array. The returned array will be padded with {@code false}, if
	 * necessary, so that it has an exact length of {@code len}.
	 *
	 * @param a the source array.
	 * @param off the starting position in the source array.
	 * @param len the number of elements to copy (returned array's length).
	 *
	 * @return a new array containing the specified range of the source array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 * @throws ArrayIndexOutOfBoundsException if {@code off} is negative.
	 * @throws NegativeArraySizeException if {@code len} is negative.
	 */
	public static boolean[] copyOf(boolean[] a, int off, int len)
	{
		boolean[] copy = new boolean[len];
		if (len > a.length - off) {
			System.arraycopy(a, off, copy, 0, a.length - off);
		} else {
			System.arraycopy(a, off, copy, 0, len);
		}
		return copy;
	}

	/**
	 * Copies the specified range of elements from the given array into a
	 * new array. The returned array will be padded with {@code 0.0}, if
	 * necessary, so that it has an exact length of {@code len}.
	 *
	 * @param a the source array.
	 * @param off the starting position in the source array.
	 * @param len the number of elements to copy (returned array's length).
	 *
	 * @return a new array containing the specified range of the source array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 * @throws ArrayIndexOutOfBoundsException if {@code off} is negative.
	 * @throws NegativeArraySizeException if {@code len} is negative.
	 */
	public static float[] copyOf(float[] a, int off, int len)
	{
		float[] copy = new float[len];
		if (len > a.length - off) {
			System.arraycopy(a, off, copy, 0, a.length - off);
		} else {
			System.arraycopy(a, off, copy, 0, len);
		}
		return copy;
	}

	/**
	 * Copies the specified range of elements from the given array into a
	 * new array. The returned array will be padded with {@code 0.0}, if
	 * necessary, so that it has an exact length of {@code len}.
	 *
	 * @param a the source array.
	 * @param off the starting position in the source array.
	 * @param len the number of elements to copy (returned array's length).
	 *
	 * @return a new array containing the specified range of the source array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 * @throws ArrayIndexOutOfBoundsException if {@code off} is negative.
	 * @throws NegativeArraySizeException if {@code len} is negative.
	 */
	public static double[] copyOf(double[] a, int off, int len)
	{
		double[] copy = new double[len];
		if (len > a.length - off) {
			System.arraycopy(a, off, copy, 0, a.length - off);
		} else {
			System.arraycopy(a, off, copy, 0, len);
		}
		return copy;
	}

	/**
	 * Copies the specified range of elements from the given array into a
	 * new array. The returned array will be padded with {@code null}, if
	 * necessary, so that it has an exact length of {@code len}.
	 *
	 * @param <T> the type of the elements in the array to copy.
	 * @param a the source array.
	 * @param off the starting position in the source array.
	 * @param len the number of elements to copy (returned array's length).
	 *
	 * @return a new array containing the specified range of the source array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 * @throws ArrayIndexOutOfBoundsException if {@code off} is negative.
	 * @throws NegativeArraySizeException if {@code len} is negative.
	 */
	public static <T> T[] copyOf(T[] a, int off, int len)
	{
		T[] copy = newArray(a.getClass().getComponentType(), len);
		if (len > a.length - off) {
			System.arraycopy(a, off, copy, 0, a.length - off);
		} else {
			System.arraycopy(a, off, copy, 0, len);
		}
		return copy;
	}

	/**
	 * Returns a copy of the given array. The original array and the
	 * returned copy will have identical length and content.
	 *
	 * @param original the array to be copied.
	 *
	 * @return a copy of the original array.
	 *
	 * @throws NullPointerException if {@code original} is {@code null}.
	 */
	public static long[] copyOf(long... original)
	{
		return Arrays.copyOf(original, original.length);
	}

	/**
	 * Returns a copy of the given array. The original array and the
	 * returned copy will have identical length and content.
	 *
	 * @param original the array to be copied.
	 *
	 * @return a copy of the original array.
	 *
	 * @throws NullPointerException if {@code original} is {@code null}.
	 */
	public static int[] copyOf(int... original)
	{
		return Arrays.copyOf(original, original.length);
	}

	/**
	 * Returns a copy of the given array. The original array and the
	 * returned copy will have identical length and content.
	 *
	 * @param original the array to be copied.
	 *
	 * @return a copy of the original array.
	 *
	 * @throws NullPointerException if {@code original} is {@code null}.
	 */
	public static short[] copyOf(short... original)
	{
		return Arrays.copyOf(original, original.length);
	}

	/**
	 * Returns a copy of the given array. The original array and the
	 * returned copy will have identical length and content.
	 *
	 * @param original the array to be copied.
	 *
	 * @return a copy of the original array.
	 *
	 * @throws NullPointerException if {@code original} is {@code null}.
	 */
	public static char[] copyOf(char... original)
	{
		return Arrays.copyOf(original, original.length);
	}

	/**
	 * Returns a copy of the given array. The original array and the
	 * returned copy will have identical length and content.
	 *
	 * @param original the array to be copied.
	 *
	 * @return a copy of the original array.
	 *
	 * @throws NullPointerException if {@code original} is {@code null}.
	 */
	public static byte[] copyOf(byte... original)
	{
		return Arrays.copyOf(original, original.length);
	}

	/**
	 * Returns a copy of the given array. The original array and the
	 * returned copy will have identical length and content.
	 *
	 * @param original the array to be copied.
	 *
	 * @return a copy of the original array.
	 *
	 * @throws NullPointerException if {@code original} is {@code null}.
	 */
	public static boolean[] copyOf(boolean... original)
	{
		return Arrays.copyOf(original, original.length);
	}

	/**
	 * Returns a copy of the given array. The original array and the
	 * returned copy will have identical length and content.
	 *
	 * @param original the array to be copied.
	 *
	 * @return a copy of the original array.
	 *
	 * @throws NullPointerException if {@code original} is {@code null}.
	 */
	public static float[] copyOf(float... original)
	{
		return Arrays.copyOf(original, original.length);
	}

	/**
	 * Returns a copy of the given array. The original array and the
	 * returned copy will have identical length and content.
	 *
	 * @param original the array to be copied.
	 *
	 * @return a copy of the original array.
	 *
	 * @throws NullPointerException if {@code original} is {@code null}.
	 */
	public static double[] copyOf(double... original)
	{
		return Arrays.copyOf(original, original.length);
	}

	/**
	 * Returns a copy of the given array. The original array and the
	 * returned copy will have identical length and content.
	 *
	 * @param <T> the type of the elements in the array to copy.
	 * @param original the array to be copied.
	 *
	 * @return a copy of the original array.
	 *
	 * @throws NullPointerException if {@code original} is {@code null}.
	 */
	public static <T> T[] copyOf(T[] original)
	{
		return Arrays.copyOf(original, original.length);
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but in reverse order.
	 *
	 * @param a the array to reverse.
	 *
	 * @return the reversed array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static long[] reverse(long... a)
	{
		int len = a.length;
		long[] copy = new long[len];
		for (int i = 0; i < len; i++) {
			copy[i] = a[len - i - 1];
		}
		return copy;
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but in reverse order.
	 *
	 * @param a the array to reverse.
	 *
	 * @return the reversed array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static int[] reverse(int... a)
	{
		int len = a.length;
		int[] copy = new int[len];
		for (int i = 0; i < len; i++) {
			copy[i] = a[len - i - 1];
		}
		return copy;
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but in reverse order.
	 *
	 * @param a the array to reverse.
	 *
	 * @return the reversed array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static short[] reverse(short... a)
	{
		int len = a.length;
		short[] copy = new short[len];
		for (int i = 0; i < len; i++) {
			copy[i] = a[len - i - 1];
		}
		return copy;
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but in reverse order.
	 *
	 * @param a the array to reverse.
	 *
	 * @return the reversed array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static char[] reverse(char... a)
	{
		int len = a.length;
		char[] copy = new char[len];
		for (int i = 0; i < len; i++) {
			copy[i] = a[len - i - 1];
		}
		return copy;
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but in reverse order.
	 *
	 * @param a the array to reverse.
	 *
	 * @return the reversed array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static byte[] reverse(byte... a)
	{
		int len = a.length;
		byte[] copy = new byte[len];
		for (int i = 0; i < len; i++) {
			copy[i] = a[len - i - 1];
		}
		return copy;
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but in reverse order.
	 *
	 * @param a the array to reverse.
	 *
	 * @return the reversed array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static boolean[] reverse(boolean... a)
	{
		int len = a.length;
		boolean[] copy = new boolean[len];
		for (int i = 0; i < len; i++) {
			copy[i] = a[len - i - 1];
		}
		return copy;
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but in reverse order.
	 *
	 * @param a the array to reverse.
	 *
	 * @return the reversed array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static float[] reverse(float... a)
	{
		int len = a.length;
		float[] copy = new float[len];
		for (int i = 0; i < len; i++) {
			copy[i] = a[len - i - 1];
		}
		return copy;
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but in reverse order.
	 *
	 * @param a the array to reverse.
	 *
	 * @return the reversed array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static double[] reverse(double... a)
	{
		int len = a.length;
		double[] copy = new double[len];
		for (int i = 0; i < len; i++) {
			copy[i] = a[len - i - 1];
		}
		return copy;
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but in reverse order.
	 *
	 * @param <T> the type of the elements in the array to reverse.
	 * @param a the array to reverse.
	 *
	 * @return the reversed array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static <T> T[] reverse(T[] a)
	{
		int len = a.length;
		T[] copy = newArray(a.getClass().getComponentType(), len);
		for (int i = 0; i < len; i++) {
			copy[i] = a[len - i - 1];
		}
		return copy;
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but rotated by the given distance. The element at index {@code i} in
	 * the returned array corresponds to the element in the original array
	 * at index {@code (i - distance)} mod {@code a.length}, for all values
	 * of {@code i} between {@code 0} and {@code a.length - 1}, inclusive.
	 * Note that rotation by {@code 0} or by a multiple of {@code a.length}
	 * is a no-op.
	 *
	 * @param a the array to rotate.
	 * @param distance the distance to rotate.
	 *
	 * @return the rotated array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static long[] rotate(long[] a, int distance)
	{
		int len = a.length;
		long[] copy = new long[len];
		for (int i = 0; i < len; i++) {
			copy[i] = a[index(i - distance, len)];
		}
		return copy;
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but rotated by the given distance. The element at index {@code i} in
	 * the returned array corresponds to the element in the original array
	 * at index {@code (i - distance)} mod {@code a.length}, for all values
	 * of {@code i} between {@code 0} and {@code a.length - 1}, inclusive.
	 * Note that rotation by {@code 0} or by a multiple of {@code a.length}
	 * is a no-op.
	 *
	 * @param a the array to rotate.
	 * @param distance the distance to rotate.
	 *
	 * @return the rotated array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static int[] rotate(int[] a, int distance)
	{
		int len = a.length;
		int[] copy = new int[len];
		for (int i = 0; i < len; i++) {
			copy[i] = a[index(i - distance, len)];
		}
		return copy;
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but rotated by the given distance. The element at index {@code i} in
	 * the returned array corresponds to the element in the original array
	 * at index {@code (i - distance)} mod {@code a.length}, for all values
	 * of {@code i} between {@code 0} and {@code a.length - 1}, inclusive.
	 * Note that rotation by {@code 0} or by a multiple of {@code a.length}
	 * is a no-op.
	 *
	 * @param a the array to rotate.
	 * @param distance the distance to rotate.
	 *
	 * @return the rotated array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static short[] rotate(short[] a, int distance)
	{
		int len = a.length;
		short[] copy = new short[len];
		for (int i = 0; i < len; i++) {
			copy[i] = a[index(i - distance, len)];
		}
		return copy;
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but rotated by the given distance. The element at index {@code i} in
	 * the returned array corresponds to the element in the original array
	 * at index {@code (i - distance)} mod {@code a.length}, for all values
	 * of {@code i} between {@code 0} and {@code a.length - 1}, inclusive.
	 * Note that rotation by {@code 0} or by a multiple of {@code a.length}
	 * is a no-op.
	 *
	 * @param a the array to rotate.
	 * @param distance the distance to rotate.
	 *
	 * @return the rotated array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static char[] rotate(char[] a, int distance)
	{
		int len = a.length;
		char[] copy = new char[len];
		for (int i = 0; i < len; i++) {
			copy[i] = a[index(i - distance, len)];
		}
		return copy;
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but rotated by the given distance. The element at index {@code i} in
	 * the returned array corresponds to the element in the original array
	 * at index {@code (i - distance)} mod {@code a.length}, for all values
	 * of {@code i} between {@code 0} and {@code a.length - 1}, inclusive.
	 * Note that rotation by {@code 0} or by a multiple of {@code a.length}
	 * is a no-op.
	 *
	 * @param a the array to rotate.
	 * @param distance the distance to rotate.
	 *
	 * @return the rotated array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static byte[] rotate(byte[] a, int distance)
	{
		int len = a.length;
		byte[] copy = new byte[len];
		for (int i = 0; i < len; i++) {
			copy[i] = a[index(i - distance, len)];
		}
		return copy;
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but rotated by the given distance. The element at index {@code i} in
	 * the returned array corresponds to the element in the original array
	 * at index {@code (i - distance)} mod {@code a.length}, for all values
	 * of {@code i} between {@code 0} and {@code a.length - 1}, inclusive.
	 * Note that rotation by {@code 0} or by a multiple of {@code a.length}
	 * is a no-op.
	 *
	 * @param a the array to rotate.
	 * @param distance the distance to rotate.
	 *
	 * @return the rotated array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static boolean[] rotate(boolean[] a, int distance)
	{
		int len = a.length;
		boolean[] copy = new boolean[len];
		for (int i = 0; i < len; i++) {
			copy[i] = a[index(i - distance, len)];
		}
		return copy;
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but rotated by the given distance. The element at index {@code i} in
	 * the returned array corresponds to the element in the original array
	 * at index {@code (i - distance)} mod {@code a.length}, for all values
	 * of {@code i} between {@code 0} and {@code a.length - 1}, inclusive.
	 * Note that rotation by {@code 0} or by a multiple of {@code a.length}
	 * is a no-op.
	 *
	 * @param a the array to rotate.
	 * @param distance the distance to rotate.
	 *
	 * @return the rotated array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static float[] rotate(float[] a, int distance)
	{
		int len = a.length;
		float[] copy = new float[len];
		for (int i = 0; i < len; i++) {
			copy[i] = a[index(i - distance, len)];
		}
		return copy;
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but rotated by the given distance. The element at index {@code i} in
	 * the returned array corresponds to the element in the original array
	 * at index {@code (i - distance)} mod {@code a.length}, for all values
	 * of {@code i} between {@code 0} and {@code a.length - 1}, inclusive.
	 * Note that rotation by {@code 0} or by a multiple of {@code a.length}
	 * is a no-op.
	 *
	 * @param a the array to rotate.
	 * @param distance the distance to rotate.
	 *
	 * @return the rotated array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static double[] rotate(double[] a, int distance)
	{
		int len = a.length;
		double[] copy = new double[len];
		for (int i = 0; i < len; i++) {
			copy[i] = a[index(i - distance, len)];
		}
		return copy;
	}

	/**
	 * Returns a new array containing the same elements as the given one,
	 * but rotated by the given distance. The element at index {@code i} in
	 * the returned array corresponds to the element in the original array
	 * at index {@code (i - distance)} mod {@code a.length}, for all values
	 * of {@code i} between {@code 0} and {@code a.length - 1}, inclusive.
	 * Note that rotation by {@code 0} or by a multiple of {@code a.length}
	 * is a no-op.
	 *
	 * @param <T> the type of the elements in the array to rotate.
	 * @param a the array to rotate.
	 * @param distance the distance to rotate.
	 *
	 * @return the rotated array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static <T> T[] rotate(T[] a, int distance)
	{
		int len = a.length;
		T[] copy = newArray(a.getClass().getComponentType(), len);
		for (int i = 0; i < len; i++) {
			copy[i] = a[index(i - distance, len)];
		}
		return copy;
	}

	private static int index(int x, int mod)
	{
		return x < 0 ? index(x + mod, mod) : x % mod;
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted. This method uses the optimized version of the Fisher-Yates
	 * shuffle algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs
	 * in linear time.
	 *
	 * @param a the array whose content will be shuffled.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static long[] shuffle(long... a)
	{
		return shuffle(a, PRNG);
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted using the specified source of randomness. All permutations
	 * occur with equal likelihood assuming that the source of randomness is
	 * fair. This method uses the optimized version of the Fisher-Yates shuffle
	 * algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs in linear
	 * time.
	 *
	 * @param a the array whose content will be shuffled.
	 * @param rnd the source of randomness to use.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if one of the arguments is {@code null}.
	 */
	public static long[] shuffle(long[] a, Random rnd)
	{
		long[] copy = copyOf(a);
		for (int i = copy.length; i > 1; i--) {
			swap(copy, i - 1, rnd.nextInt(i));
		}
		return copy;
	}

	private static void swap(long[] a, int i, int j)
	{
		long c = a[i];
		a[i] = a[j];
		a[j] = c;
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted. This method uses the optimized version of the Fisher-Yates
	 * shuffle algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs
	 * in linear time.
	 *
	 * @param a the array whose content will be shuffled.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static int[] shuffle(int... a)
	{
		return shuffle(a, PRNG);
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted using the specified source of randomness. All permutations
	 * occur with equal likelihood assuming that the source of randomness is
	 * fair. This method uses the optimized version of the Fisher-Yates shuffle
	 * algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs in linear
	 * time.
	 *
	 * @param a the array whose content will be shuffled.
	 * @param rnd the source of randomness to use.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if one of the arguments is {@code null}.
	 */
	public static int[] shuffle(int[] a, Random rnd)
	{
		int[] copy = copyOf(a);
		for (int i = copy.length; i > 1; i--) {
			swap(copy, i - 1, rnd.nextInt(i));
		}
		return copy;
	}

	private static void swap(int[] a, int i, int j)
	{
		int c = a[i];
		a[i] = a[j];
		a[j] = c;
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted. This method uses the optimized version of the Fisher-Yates
	 * shuffle algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs
	 * in linear time.
	 *
	 * @param a the array whose content will be shuffled.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static short[] shuffle(short... a)
	{
		return shuffle(a, PRNG);
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted using the specified source of randomness. All permutations
	 * occur with equal likelihood assuming that the source of randomness is
	 * fair. This method uses the optimized version of the Fisher-Yates shuffle
	 * algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs in linear
	 * time.
	 *
	 * @param a the array whose content will be shuffled.
	 * @param rnd the source of randomness to use.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if one of the arguments is {@code null}.
	 */
	public static short[] shuffle(short[] a, Random rnd)
	{
		short[] copy = copyOf(a);
		for (int i = copy.length; i > 1; i--) {
			swap(copy, i - 1, rnd.nextInt(i));
		}
		return copy;
	}

	private static void swap(short[] a, int i, int j)
	{
		short c = a[i];
		a[i] = a[j];
		a[j] = c;
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted. This method uses the optimized version of the Fisher-Yates
	 * shuffle algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs
	 * in linear time.
	 *
	 * @param a the array whose content will be shuffled.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static char[] shuffle(char... a)
	{
		return shuffle(a, PRNG);
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted using the specified source of randomness. All permutations
	 * occur with equal likelihood assuming that the source of randomness is
	 * fair. This method uses the optimized version of the Fisher-Yates shuffle
	 * algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs in linear
	 * time.
	 *
	 * @param a the array whose content will be shuffled.
	 * @param rnd the source of randomness to use.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if one of the arguments is {@code null}.
	 */
	public static char[] shuffle(char[] a, Random rnd)
	{
		char[] copy = copyOf(a);
		for (int i = copy.length; i > 1; i--) {
			swap(copy, i - 1, rnd.nextInt(i));
		}
		return copy;
	}

	private static void swap(char[] a, int i, int j)
	{
		char c = a[i];
		a[i] = a[j];
		a[j] = c;
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted. This method uses the optimized version of the Fisher-Yates
	 * shuffle algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs
	 * in linear time.
	 *
	 * @param a the array whose content will be shuffled.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static byte[] shuffle(byte... a)
	{
		return shuffle(a, PRNG);
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted using the specified source of randomness. All permutations
	 * occur with equal likelihood assuming that the source of randomness is
	 * fair. This method uses the optimized version of the Fisher-Yates shuffle
	 * algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs in linear
	 * time.
	 *
	 * @param a the array whose content will be shuffled.
	 * @param rnd the source of randomness to use.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if one of the arguments is {@code null}.
	 */
	public static byte[] shuffle(byte[] a, Random rnd)
	{
		byte[] copy = copyOf(a);
		for (int i = copy.length; i > 1; i--) {
			swap(copy, i - 1, rnd.nextInt(i));
		}
		return copy;
	}

	private static void swap(byte[] a, int i, int j)
	{
		byte c = a[i];
		a[i] = a[j];
		a[j] = c;
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted. This method uses the optimized version of the Fisher-Yates
	 * shuffle algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs
	 * in linear time.
	 *
	 * @param a the array whose content will be shuffled.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static boolean[] shuffle(boolean... a)
	{
		return shuffle(a, PRNG);
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted using the specified source of randomness. All permutations
	 * occur with equal likelihood assuming that the source of randomness is
	 * fair. This method uses the optimized version of the Fisher-Yates shuffle
	 * algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs in linear
	 * time.
	 *
	 * @param a the array whose content will be shuffled.
	 * @param rnd the source of randomness to use.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if one of the arguments is {@code null}.
	 */
	public static boolean[] shuffle(boolean[] a, Random rnd)
	{
		boolean[] copy = copyOf(a);
		for (int i = copy.length; i > 1; i--) {
			swap(copy, i - 1, rnd.nextInt(i));
		}
		return copy;
	}

	private static void swap(boolean[] a, int i, int j)
	{
		boolean c = a[i];
		a[i] = a[j];
		a[j] = c;
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted. This method uses the optimized version of the Fisher-Yates
	 * shuffle algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs
	 * in linear time.
	 *
	 * @param a the array whose content will be shuffled.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static float[] shuffle(float... a)
	{
		return shuffle(a, PRNG);
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted using the specified source of randomness. All permutations
	 * occur with equal likelihood assuming that the source of randomness is
	 * fair. This method uses the optimized version of the Fisher-Yates shuffle
	 * algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs in linear
	 * time.
	 *
	 * @param a the array whose content will be shuffled.
	 * @param rnd the source of randomness to use.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if one of the arguments is {@code null}.
	 */
	public static float[] shuffle(float[] a, Random rnd)
	{
		float[] copy = copyOf(a);
		for (int i = copy.length; i > 1; i--) {
			swap(copy, i - 1, rnd.nextInt(i));
		}
		return copy;
	}

	private static void swap(float[] a, int i, int j)
	{
		float c = a[i];
		a[i] = a[j];
		a[j] = c;
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted. This method uses the optimized version of the Fisher-Yates
	 * shuffle algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs
	 * in linear time.
	 *
	 * @param a the array whose content will be shuffled.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static double[] shuffle(double... a)
	{
		return shuffle(a, PRNG);
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted using the specified source of randomness. All permutations
	 * occur with equal likelihood assuming that the source of randomness is
	 * fair. This method uses the optimized version of the Fisher-Yates shuffle
	 * algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs in linear
	 * time.
	 *
	 * @param a the array whose content will be shuffled.
	 * @param rnd the source of randomness to use.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if one of the arguments is {@code null}.
	 */
	public static double[] shuffle(double[] a, Random rnd)
	{
		double[] copy = copyOf(a);
		for (int i = copy.length; i > 1; i--) {
			swap(copy, i - 1, rnd.nextInt(i));
		}
		return copy;
	}

	private static void swap(double[] a, int i, int j)
	{
		double c = a[i];
		a[i] = a[j];
		a[j] = c;
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted. This method uses the optimized version of the Fisher-Yates
	 * shuffle algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs
	 * in linear time.
	 *
	 * @param <T> the type of the elements in the array to shuffle.
	 * @param a the array whose content will be shuffled.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 */
	public static <T> T[] shuffle(T[] a)
	{
		return shuffle(a, PRNG);
	}

	/**
	 * Returns a copy of the given array where its element have been randomly
	 * permuted using the specified source of randomness. All permutations
	 * occur with equal likelihood assuming that the source of randomness is
	 * fair. This method uses the optimized version of the Fisher-Yates shuffle
	 * algorithm (Fisher, Yates, Durstenfeld, Knuth) and thus runs in linear
	 * time.
	 *
	 * @param <T> the type of the elements in the array to shuffle.
	 * @param a the array whose content will be shuffled.
	 * @param rnd the source of randomness to use.
	 *
	 * @return the shuffled array.
	 *
	 * @throws NullPointerException if one of the arguments is {@code null}.
	 */
	public static <T> T[] shuffle(T[] a, Random rnd)
	{
		T[] copy = copyOf(a);
		for (int i = copy.length; i > 1; i--) {
			swap(copy, i - 1, rnd.nextInt(i));
		}
		return copy;
	}

	private static <T> void swap(T[] a, int i, int j)
	{
		T c = a[i];
		a[i] = a[j];
		a[j] = c;
	}

	/**
	 * Returns a sorted copy of the given array into ascending numerical
	 * order.
	 *
	 * @param a the array to be sorted.
	 *
	 * @return a sorted copy of the given array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 *
	 * @see Arrays#sort(long[])
	 */
	public static long[] sort(long... a)
	{
		long[] copy = copyOf(a);
		Arrays.sort(copy);
		return copy;
	}

	/**
	 * Returns a sorted copy of the given array into ascending numerical
	 * order.
	 *
	 * @param a the array to be sorted.
	 *
	 * @return a sorted copy of the given array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 *
	 * @see Arrays#sort(int[])
	 */
	public static int[] sort(int... a)
	{
		int[] copy = copyOf(a);
		Arrays.sort(copy);
		return copy;
	}

	/**
	 * Returns a sorted copy of the given array into ascending numerical
	 * order.
	 *
	 * @param a the array to be sorted.
	 *
	 * @return a sorted copy of the given array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 *
	 * @see Arrays#sort(short[])
	 */
	public static short[] sort(short... a)
	{
		short[] copy = copyOf(a);
		Arrays.sort(copy);
		return copy;
	}

	/**
	 * Returns a sorted copy of the given array into ascending numerical
	 * order.
	 *
	 * @param a the array to be sorted.
	 *
	 * @return a sorted copy of the given array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 *
	 * @see Arrays#sort(char[])
	 */
	public static char[] sort(char... a)
	{
		char[] copy = copyOf(a);
		Arrays.sort(copy);
		return copy;
	}

	/**
	 * Returns a sorted copy of the given array into ascending numerical
	 * order.
	 *
	 * @param a the array to be sorted.
	 *
	 * @return a sorted copy of the given array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 *
	 * @see Arrays#sort(byte[])
	 */
	public static byte[] sort(byte... a)
	{
		byte[] copy = copyOf(a);
		Arrays.sort(copy);
		return copy;
	}

	/**
	 * Returns a sorted copy of the given array into ascending numerical
	 * order.
	 *
	 * @param a the array to be sorted.
	 *
	 * @return a sorted copy of the given array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 *
	 * @see Arrays#sort(float[])
	 */
	public static float[] sort(float... a)
	{
		float[] copy = copyOf(a);
		Arrays.sort(copy);
		return copy;
	}

	/**
	 * Returns a sorted copy of the given array into ascending numerical
	 * order.
	 *
	 * @param a the array to be sorted.
	 *
	 * @return a sorted copy of the given array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 *
	 * @see Arrays#sort(double[])
	 */
	public static double[] sort(double... a)
	{
		double[] copy = copyOf(a);
		Arrays.sort(copy);
		return copy;
	}

	/**
	 * Returns a sorted copy of the given array of objects into ascending
	 * order, according to the natural ordering of its elements (all
	 * elements in the array must implement the {@link Comparable}
	 * interface).
	 *
	 * @param <T> the type of the elements in the array to sort.
	 * @param a the array to be sorted.
	 *
	 * @return a sorted copy of the given array.
	 *
	 * @throws NullPointerException if {@code a} is {@code null}.
	 * @throws ClassCastException if the array contains elements that are
	 *	not mutually comparable (for example, Strings and Integers).
	 * @throws IllegalArgumentException if the natural ordering of the array
	 *	elements is found to violate the {@link Comparable} contract.
	 *
	 * @see Arrays#sort(java.lang.Object[])
	 */
	public static <T> T[] sort(T[] a)
	{
		T[] copy = copyOf(a);
		Arrays.sort(copy);
		return copy;
	}

	/**
	 * Returns a sorted copy of the given array of objects into ascending
	 * order, according to the order induced by the given {@code Comparator}.
	 *
	 * @param <T> the type of the elements in the array to sort.
	 * @param a the array to be sorted.
	 * @param c the comparator to determine the order of the array.
	 *
	 * @return a sorted copy of the given array.
	 *
	 * @throws NullPointerException if {@code a} or {@code c} is {@code null}.
	 * @throws ClassCastException if the array contains elements that are
	 *	not mutually comparable (for example, Strings and Integers).
	 *
	 * @see Arrays#sort(java.lang.Object[], java.util.Comparator)
	 */
	public static <T> T[] sort(T[] a, Comparator<? super T> c)
	{
		T[] copy = Arrays.copyOf(a, a.length);
		Arrays.sort(copy, c);
		return copy;
	}

	/**
	 * Converts an array of {@code Long}s to an array of {@code long}s.
	 * Returns {@code null} if the given array is {@code null}. Does not
	 * modify the input array.
	 *
	 * @param a the {@code Long} array to convert.
	 *
	 * @return the corresponding {@code long} array.
	 *
	 * @throws NullPointerException if {@code a} contains a {@code null} value.
	 */
	public static long[] toPrimitive(Long... a)
	{
		if (a != null) {
			long[] p = new long[a.length];
			for (int i = 0; i < a.length; i++) {
				p[i] = a[i];
			}
			return p;
		}
		return null;
	}

	/**
	 * Converts an array of {@code Integer}s to an array of {@code int}s.
	 * Returns {@code null} if the given array is {@code null}. Does not
	 * modify the input array.
	 *
	 * @param a the {@code Integer} array to convert.
	 *
	 * @return the corresponding {@code int} array.
	 *
	 * @throws NullPointerException if {@code a} contains a {@code null} value.
	 */
	public static int[] toPrimitive(Integer... a)
	{
		if (a != null) {
			int[] p = new int[a.length];
			for (int i = 0; i < a.length; i++) {
				p[i] = a[i];
			}
			return p;
		}
		return null;
	}

	/**
	 * Converts an array of {@code Short}s to an array of {@code short}s.
	 * Returns {@code null} if the given array is {@code null}. Does not
	 * modify the input array.
	 *
	 * @param a the {@code Short} array to convert.
	 *
	 * @return the corresponding {@code short} array.
	 *
	 * @throws NullPointerException if {@code a} contains a {@code null} value.
	 */
	public static short[] toPrimitive(Short... a)
	{
		if (a != null) {
			short[] p = new short[a.length];
			for (int i = 0; i < a.length; i++) {
				p[i] = a[i];
			}
			return p;
		}
		return null;
	}

	/**
	 * Converts an array of {@code Character}s to an array of {@code char}s.
	 * Returns {@code null} if the given array is {@code null}. Does not
	 * modify the input array.
	 *
	 * @param a the {@code Character} array to convert.
	 *
	 * @return the corresponding {@code char} array.
	 *
	 * @throws NullPointerException if {@code a} contains a {@code null} value.
	 */
	public static char[] toPrimitive(Character... a)
	{
		if (a != null) {
			char[] p = new char[a.length];
			for (int i = 0; i < a.length; i++) {
				p[i] = a[i];
			}
			return p;
		}
		return null;
	}

	/**
	 * Converts an array of {@code Byte}s to an array of {@code byte}s.
	 * Returns {@code null} if the given array is {@code null}. Does not
	 * modify the input array.
	 *
	 * @param a the {@code Byte} array to convert.
	 *
	 * @return the corresponding {@code byte} array.
	 *
	 * @throws NullPointerException if {@code a} contains a {@code null} value.
	 */
	public static byte[] toPrimitive(Byte... a)
	{
		if (a != null) {
			byte[] p = new byte[a.length];
			for (int i = 0; i < a.length; i++) {
				p[i] = a[i];
			}
			return p;
		}
		return null;
	}

	/**
	 * Converts an array of {@code Boolean}s to an array of {@code boolean}s.
	 * Returns {@code null} if the given array is {@code null}. Does not
	 * modify the input array.
	 *
	 * @param a the {@code Boolean} array to convert.
	 *
	 * @return the corresponding {@code boolean} array.
	 *
	 * @throws NullPointerException if {@code a} contains a {@code null} value.
	 */
	public static boolean[] toPrimitive(Boolean... a)
	{
		if (a != null) {
			boolean[] p = new boolean[a.length];
			for (int i = 0; i < a.length; i++) {
				p[i] = a[i];
			}
			return p;
		}
		return null;
	}

	/**
	 * Converts an array of {@code Float}s to an array of {@code float}s.
	 * Returns {@code null} if the given array is {@code null}. Does not
	 * modify the input array.
	 *
	 * @param a the {@code Float} array to convert.
	 *
	 * @return the corresponding {@code float} array.
	 *
	 * @throws NullPointerException if {@code a} contains a {@code null} value.
	 */
	public static float[] toPrimitive(Float... a)
	{
		if (a != null) {
			float[] p = new float[a.length];
			for (int i = 0; i < a.length; i++) {
				p[i] = a[i];
			}
			return p;
		}
		return null;
	}

	/**
	 * Converts an array of {@code Double}s to an array of {@code double}s.
	 * Returns {@code null} if the given array is {@code null}. Does not
	 * modify the input array.
	 *
	 * @param a the {@code Double} array to convert.
	 *
	 * @return the corresponding {@code double} array.
	 *
	 * @throws NullPointerException if {@code a} contains a {@code null} value.
	 */
	public static double[] toPrimitive(Double... a)
	{
		if (a != null) {
			double[] p = new double[a.length];
			for (int i = 0; i < a.length; i++) {
				p[i] = a[i];
			}
			return p;
		}
		return null;
	}

	/**
	 * Converts an array of {@code long}s to an array of {@code Long}s.
	 * Returns {@code null} if the given array is {@code null}. Does not
	 * modify the input array.
	 *
	 * @param a the {@code long} array to convert.
	 *
	 * @return the corresponding {@code Long} array.
	 */
	public static Long[] toObject(long... a)
	{
		if (a != null) {
			Long[] w = new Long[a.length];
			for (int i = 0; i < a.length; i++) {
				w[i] = a[i];
			}
			return w;
		}
		return null;
	}

	/**
	 * Converts an array of {@code int}s to an array of {@code Integer}s.
	 * Returns {@code null} if the given array is {@code null}. Does not
	 * modify the input array.
	 *
	 * @param a the {@code int} array to convert.
	 *
	 * @return the corresponding {@code Integer} array.
	 */
	public static Integer[] toObject(int... a)
	{
		if (a != null) {
			Integer[] w = new Integer[a.length];
			for (int i = 0; i < a.length; i++) {
				w[i] = a[i];
			}
			return w;
		}
		return null;
	}

	/**
	 * Converts an array of {@code short}s to an array of {@code Short}s.
	 * Returns {@code null} if the given array is {@code null}. Does not
	 * modify the input array.
	 *
	 * @param a the {@code short} array to convert.
	 *
	 * @return the corresponding {@code Short} array.
	 */
	public static Short[] toObject(short... a)
	{
		if (a != null) {
			Short[] w = new Short[a.length];
			for (int i = 0; i < a.length; i++) {
				w[i] = a[i];
			}
			return w;
		}
		return null;
	}

	/**
	 * Converts an array of {@code char}s to an array of {@code Character}s.
	 * Returns {@code null} if the given array is {@code null}. Does not
	 * modify the input array.
	 *
	 * @param a the {@code char} array to convert.
	 *
	 * @return the corresponding {@code Character} array.
	 */
	public static Character[] toObject(char... a)
	{
		if (a != null) {
			Character[] w = new Character[a.length];
			for (int i = 0; i < a.length; i++) {
				w[i] = a[i];
			}
			return w;
		}
		return null;
	}

	/**
	 * Converts an array of {@code byte}s to an array of {@code Byte}s.
	 * Returns {@code null} if the given array is {@code null}. Does not
	 * modify the input array.
	 *
	 * @param a the {@code byte} array to convert.
	 *
	 * @return the corresponding {@code Byte} array.
	 */
	public static Byte[] toObject(byte... a)
	{
		if (a != null) {
			Byte[] w = new Byte[a.length];
			for (int i = 0; i < a.length; i++) {
				w[i] = a[i];
			}
			return w;
		}
		return null;
	}

	/**
	 * Converts an array of {@code boolean}s to an array of {@code Boolean}s.
	 * Returns {@code null} if the given array is {@code null}. Does not
	 * modify the input array.
	 *
	 * @param a the {@code boolean} array to convert.
	 *
	 * @return the corresponding {@code Boolean} array.
	 */
	public static Boolean[] toObject(boolean... a)
	{
		if (a != null) {
			Boolean[] w = new Boolean[a.length];
			for (int i = 0; i < a.length; i++) {
				w[i] = a[i];
			}
			return w;
		}
		return null;
	}

	/**
	 * Converts an array of {@code float}s to an array of {@code Float}s.
	 * Returns {@code null} if the given array is {@code null}. Does not
	 * modify the input array.
	 *
	 * @param a the {@code Float} array to convert.
	 *
	 * @return the corresponding {@code Short} array.
	 */
	public static Float[] toObject(float... a)
	{
		if (a != null) {
			Float[] w = new Float[a.length];
			for (int i = 0; i < a.length; i++) {
				w[i] = a[i];
			}
			return w;
		}
		return null;
	}

	/**
	 * Converts an array of {@code double}s to an array of {@code Double}s.
	 * Returns {@code null} if the given array is {@code null}. Does not
	 * modify the input array.
	 *
	 * @param a the {@code double} array to convert.
	 *
	 * @return the corresponding {@code Double} array.
	 */
	public static Double[] toObject(double... a)
	{
		if (a != null) {
			Double[] w = new Double[a.length];
			for (int i = 0; i < a.length; i++) {
				w[i] = a[i];
			}
			return w;
		}
		return null;
	}

	/**
	 * Returns a {@code String} representation of the contents of the given
	 * array. Returns {@code "null"} if {@code a} is {@code null}.
	 *
	 * @param a the array whose {@code String} representation to return.
	 *
	 * @return a {@code String} representation of {@code a}.
	 *
	 * @see Arrays#toString(long[])
	 */
	public static String toString(long... a)
	{
		return Arrays.toString(a);
	}

	/**
	 * Returns a {@code String} representation of the contents of the given
	 * array. Returns {@code "null"} if {@code a} is {@code null}.
	 *
	 * @param a the array whose {@code String} representation to return.
	 *
	 * @return a {@code String} representation of {@code a}.
	 *
	 * @see Arrays#toString(int[])
	 */
	public static String toString(int... a)
	{
		return Arrays.toString(a);
	}

	/**
	 * Returns a {@code String} representation of the contents of the given
	 * array. Returns {@code "null"} if {@code a} is {@code null}.
	 *
	 * @param a the array whose {@code String} representation to return.
	 *
	 * @return a {@code String} representation of {@code a}.
	 *
	 * @see Arrays#toString(short[])
	 */
	public static String toString(short... a)
	{
		return Arrays.toString(a);
	}

	/**
	 * Returns a {@code String} representation of the contents of the given
	 * array. Returns {@code "null"} if {@code a} is {@code null}.
	 *
	 * @param a the array whose {@code String} representation to return.
	 *
	 * @return a {@code String} representation of {@code a}.
	 *
	 * @see Arrays#toString(char[])
	 */
	public static String toString(char... a)
	{
		return Arrays.toString(a);
	}

	/**
	 * Returns a {@code String} representation of the contents of the given
	 * array. Returns {@code "null"} if {@code a} is {@code null}.
	 *
	 * @param a the array whose {@code String} representation to return.
	 *
	 * @return a {@code String} representation of {@code a}.
	 *
	 * @see Arrays#toString(byte[])
	 */
	public static String toString(byte... a)
	{
		return Arrays.toString(a);
	}

	/**
	 * Returns a {@code String} representation of the contents of the given
	 * array. Returns {@code "null"} if {@code a} is {@code null}.
	 *
	 * @param a the array whose {@code String} representation to return.
	 *
	 * @return a {@code String} representation of {@code a}.
	 *
	 * @see Arrays#toString(boolean[])
	 */
	public static String toString(boolean... a)
	{
		return Arrays.toString(a);
	}

	/**
	 * Returns a {@code String} representation of the contents of the given
	 * array. Returns {@code "null"} if {@code a} is {@code null}.
	 *
	 * @param a the array whose {@code String} representation to return.
	 *
	 * @return a {@code String} representation of {@code a}.
	 *
	 * @see Arrays#toString(float[])
	 */
	public static String toString(float... a)
	{
		return Arrays.toString(a);
	}

	/**
	 * Returns a {@code String} representation of the contents of the given
	 * array. Returns {@code "null"} if {@code a} is {@code null}.
	 *
	 * @param a the array whose {@code String} representation to return.
	 *
	 * @return a {@code String} representation of {@code a}.
	 *
	 * @see Arrays#toString(double[])
	 */
	public static String toString(double... a)
	{
		return Arrays.toString(a);
	}

	/**
	 * Returns a {@code String} representation of the contents of the given
	 * array. Accepts multidimensional arrays. Returns {@code "null"} if
	 * {@code a} is {@code null}.
	 *
	 * @param a the array whose {@code String} representation to return.
	 *
	 * @return a {@code String} representation of {@code a}.
	 *
	 * @see Arrays#deepToString(java.lang.Object[])
	 */
	public static String toString(Object[] a)
	{
		return Arrays.deepToString(a);
	}

	private static <T> T[] newArray(Class<?> componentType, int len)
	{
		return (T[]) Array.newInstance(componentType, len);
	}

	private XArrays()
	{
		/* ... */
	}
}


/*----------------------------------------------------------------------------*
 * This file is part of Pitaya.                                               *
 * Copyright (C) 2012-2016 Osman KOCAK <kocakosm@gmail.com>                   *
 *                                                                            *
 * This program is free software: you can redistribute it and/or modify it    *
 * under the terms of the GNU Lesser General Public License as published by   *
 * the Free Software Foundation, either version 3 of the License, or (at your *
 * option) any later version.                                                 *
 * This program 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 Lesser General Public     *
 * License for more details.                                                  *
 * You should have received a copy of the GNU Lesser General Public License   *
 * along with this program. If not, see <http://www.gnu.org/licenses/>.       *
 *----------------------------------------------------------------------------*/

package org.kocakosm.pitaya.charset;

import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CharacterCodingException;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;

/**
 * Static utility methods that operate on or return ASCII {@code char}s and
 * {@code String}s.
 *
 * @author Osman KOCAK
 */
public final class ASCII
{
	/**
	 * Returns whether the given {@code String} can be encoded into ASCII.
	 *
	 * @param str the {@code String} to test.
	 *
	 * @return whether {@code str} can be encoded into ASCII.
	 *
	 * @throws NullPointerException if {@code str} is {@code null}.
	 */
	public static boolean canEncode(String str)
	{
		return Charsets.US_ASCII.newEncoder().canEncode(str);
	}

	/**
	 * Returns whether the specified range in the given {@code String} can
	 * be encoded into ASCII.
	 *
	 * @param str the input {@code String}.
	 * @param off the start index, inclusive.
	 * @param len the number of characters to test.
	 *
	 * @return whether the specified range in {@code str} can be encoded
	 *	into ASCII.
	 *
	 * @throws NullPointerException if {@code str} is {@code null}.
	 * @throws IndexOutOfBoundsException if {@code off} or {@code len} is
	 *	negative or if {@code off + len} is greater than {@code str}'s
	 *	length.
	 */
	public static boolean canEncode(String str, int off, int len)
	{
		return canEncode(str.substring(off, off + len));
	}

	/**
	 * Returns whether the given byte array represents valid ASCII encoded
	 * characters.
	 *
	 * @param input the bytes to test.
	 *
	 * @return whether {@code input} repesents ASCII encoded characters.
	 *
	 * @throws NullPointerException if {@code input} is {@code null}.
	 */
	public static boolean canDecode(byte... input)
	{
		return canDecode(input, 0, input.length);
	}

	/**
	 * Returns whether the specified range in the given byte array represents
	 * valid ASCII encoded characters.
	 *
	 * @param input the input buffer.
	 * @param off the start index, inclusive.
	 * @param len the number of bytes to test.
	 *
	 * @return whether the specified range in {@code input} represents ASCII
	 *	encoded characters.
	 *
	 * @throws NullPointerException if {@code input} is {@code null}.
	 * @throws IndexOutOfBoundsException if {@code off} or {@code len} is
	 *	negative or if {@code off + len} is greater than {@code input}'s
	 *	length.
	 */
	public static boolean canDecode(byte[] input, int off, int len)
	{
		try {
			decode(input, off, len);
		} catch (IllegalArgumentException ex) {
			return false;
		}
		return true;
	}

	/**
	 * Returns the ASCII encoding of the given {@code String}.
	 *
	 * @param str the {@code String} to encode.
	 *
	 * @return the ASCII encoding of the given {@code String}.
	 *
	 * @throws NullPointerException if {@code str} is {@code null}.
	 * @throws IllegalArgumentException if {@code str} can't be encoded into
	 *	ASCII.
	 */
	public static byte[] encode(String str)
	{
		CharsetEncoder encoder = Charsets.US_ASCII.newEncoder();
		try {
			ByteBuffer out = encoder.encode(CharBuffer.wrap(str));
			byte[] bytes = new byte[out.limit()];
			out.get(bytes);
			return bytes;
		} catch (CharacterCodingException ex) {
			throw new IllegalArgumentException(ex);
		}
	}

	/**
	 * Returns the ASCII encoding of the specified character sequence.
	 *
	 * @param str the input {@code String}.
	 * @param off the start index, inclusive.
	 * @param len the number of characters to encode.
	 *
	 * @return the ASCII encoding of the specified characters.
	 *
	 * @throws NullPointerException if {@code str} is {@code null}.
	 * @throws IndexOutOfBoundsException if {@code off} or {@code len} is
	 *	negative or if {@code off + len} is greater than {@code str}'s
	 *	length.
	 * @throws IllegalArgumentException if the specified characters can't be
	 *	encoded into ASCII.
	 */
	public static byte[] encode(String str, int off, int len)
	{
		return encode(str.substring(off, off + len));
	}

	/**
	 * Decodes the given ASCII encoded characters.
	 *
	 * @param input the ASCII encoded characters to decode.
	 *
	 * @return the decoded characters.
	 *
	 * @throws NullPointerException if {@code input} is {@code null}.
	 * @throws IllegalArgumentException if {@code input} doesn't represent
	 *	valid ASCII encoded characters.
	 */
	public static String decode(byte... input)
	{
		return decode(input, 0, input.length);
	}

	/**
	 * Decodes {@code len} ASCII encoded bytes from the given input buffer,
	 * starting at {@code off}.
	 *
	 * @param input the input buffer.
	 * @param off the starting offset.
	 * @param len the number of bytes to decode.
	 *
	 * @return the decoded characters.
	 *
	 * @throws NullPointerException if {@code input} is {@code null}.
	 * @throws IndexOutOfBoundsException if {@code off} or {@code len} is
	 *	negative or if {@code off + len} is greater than {@code input}'s
	 *	length.
	 * @throws IllegalArgumentException if the specified bytes do not
	 *	represent valid ASCII encoded characters.
	 */
	public static String decode(byte[] input, int off, int len)
	{
		CharsetDecoder decoder = Charsets.US_ASCII.newDecoder();
		ByteBuffer buf = ByteBuffer.wrap(input, off, len);
		try {
			CharBuffer out = decoder.decode(buf);
			char[] chars = new char[out.limit()];
			out.get(chars);
			return new String(chars);
		} catch (CharacterCodingException ex) {
			throw new IllegalArgumentException(ex);
		}
	}

	/**
	 * Returns whether the given character is one of the 95 printable ASCII
	 * characters.
	 *
	 * @param c the character to test.
	 *
	 * @return whether the given character is printable.
	 *
	 * @since 0.2
	 */
	public static boolean isPrintable(char c)
	{
		return c > 31 && c < 127;
	}

	/**
	 * Returns whether the given character represents a digit.
	 *
	 * @param c the character to test.
	 *
	 * @return whether the given character represents a digit.
	 */
	public static boolean isDigit(char c)
	{
		return c >= '0' && c <= '9';
	}

	/**
	 * Returns whether the given character is a letter.
	 *
	 * @param c the character to test.
	 *
	 * @return whether the given character is a letter.
	 */
	public static boolean isLetter(char c)
	{
		return isLowerCase(c) || isUpperCase(c);
	}

	/**
	 * Returns whether the given {@code String} is an alphabetic sequence,
	 * that is, a sequence which only contains letters.
	 *
	 * @param str the {@code String} to test.
	 *
	 * @return whether the given {@code String} is an alphabetic sequence.
	 *
	 * @throws NullPointerException if {@code str} is {@code null}.
	 */
	public static boolean isAlphabetic(String str)
	{
		for (char c : str.toCharArray()) {
			if (!isLetter(c)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Returns whether the given {@code String} is a numeric sequence, that
	 * is, a sequence which only contains digits.
	 *
	 * @param str the {@code String} to test.
	 *
	 * @return whether the given {@code String} is a numeric sequence.
	 *
	 * @throws NullPointerException if {@code str} is {@code null}.
	 */
	public static boolean isNumeric(String str)
	{
		for (char c : str.toCharArray()) {
			if (!isDigit(c)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Returns whether the given {@code String} is an alphanumeric sequence,
	 * that is, a sequence which only contains letters and digits.
	 *
	 * @param str the {@code String} to test.
	 *
	 * @return whether the given {@code String} is an alphanumeric sequence.
	 *
	 * @throws NullPointerException if {@code str} is {@code null}.
	 */
	public static boolean isAlphaNumeric(String str)
	{
		for (char c : str.toCharArray()) {
			if (!isDigit(c) && !isLetter(c)) {
				return false;
			}
		}
		return true;
	}

	/**
	 * Returns whether the given character is one of the lowercase ASCII
	 * characters between 'a' and 'z' (inclusive). All others return false.
	 *
	 * @param c the character to test.
	 *
	 * @return whether the given character is a lowercase character.
	 */
	public static boolean isLowerCase(char c)
	{
		return c >= 'a' && c <= 'z';
	}

	/**
	 * Returns whether the given character is one of the uppercase ASCII
	 * characters between 'A' and 'Z' (inclusive). All others return false.
	 *
	 * @param c the character to test.
	 *
	 * @return whether the given character is a uppercase character.
	 */
	public static boolean isUpperCase(char c)
	{
		return c >= 'A' && c <= 'Z';
	}

	/**
	 * Returns the lowercase equivalent of the given character. If the given
	 * character doesn't represent an uppercase ASCII character, this method
	 * simply returns the given argument.
	 *
	 * @param c the character to transform lowercase.
	 *
	 * @return the lowercase equivalent of the given character if it is an
	 *	uppercase one, or the argument itself otherwise.
	 */
	public static char toLowerCase(char c)
	{
		return isUpperCase(c) ? (char) (c + 32) : c;
	}

	/**
	 * Returns the uppercase equivalent of the given character. If the given
	 * character doesn't represent a lowercase ASCII character, this method
	 * simply returns the given argument.
	 *
	 * @param c the character to transform uppercase.
	 *
	 * @return the uppercase equivalent of the given character if it is a
	 *	lowercase one, or the argument itself otherwise.
	 */
	public static char toUpperCase(char c)
	{
		return isLowerCase(c) ? (char) (c - 32) : c;
	}

	/**
	 * Returns a {@code String} in which all uppercase ASCII characters have
	 * been replaced by their lowercase equivalent (all other characters are
	 * unchanged).
	 *
	 * @param str the {@code String} to convert into lowercase.
	 *
	 * @return the converted {@code String}.
	 *
	 * @throws NullPointerException if {@code str} is {@code null}.
	 */
	public static String toLowerCase(String str)
	{
		StringBuilder sb = new StringBuilder(str.length());
		for (char c : str.toCharArray()) {
			sb.append(toLowerCase(c));
		}
		return sb.toString();
	}

	/**
	 * Returns a {@code String} in which all lowercase ASCII characters have
	 * been replaced by their uppercase equivalent (all other characters are
	 * unchanged).
	 *
	 * @param str the {@code String} to convert into uppercase.
	 *
	 * @return the converted {@code String}.
	 *
	 * @throws NullPointerException if {@code str} is {@code null}.
	 */
	public static String toUpperCase(String str)
	{
		StringBuilder sb = new StringBuilder(str.length());
		for (char c : str.toCharArray()) {
			sb.append(toUpperCase(c));
		}
		return sb.toString();
	}

	/**
	 * Returns a {@code String} built from the given one by upper-casing its
	 * first character (other characters are copied unchanged).
	 *
	 * @param str the {@code String} to capitalize.
	 *
	 * @return the capitalized {@code String}.
	 *
	 * @throws NullPointerException if {@code str} is {@code null}.
	 */
	public static String capitalize(String str)
	{
		StringBuilder sb = new StringBuilder(str.length());
		for (char c : str.toCharArray()) {
			sb.append(sb.length() > 0 ? c : toUpperCase(c));
		}
		return sb.toString();
	}

	private ASCII()
	{
		/* ... */
	}
}


/*----------------------------------------------------------------------------*
 * This file is part of Pitaya.                                               *
 * Copyright (C) 2012-2016 Osman KOCAK <kocakosm@gmail.com>                   *
 *                                                                            *
 * This program is free software: you can redistribute it and/or modify it    *
 * under the terms of the GNU Lesser General Public License as published by   *
 * the Free Software Foundation, either version 3 of the License, or (at your *
 * option) any later version.                                                 *
 * This program 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 Lesser General Public     *
 * License for more details.                                                  *
 * You should have received a copy of the GNU Lesser General Public License   *
 * along with this program. If not, see <http://www.gnu.org/licenses/>.       *
 *----------------------------------------------------------------------------*/

package org.kocakosm.pitaya.util;

/**
 * RFC 4648 encoding scheme. Instances of this class are immutable.
 *
 * @see RFC 4648
 *
 * @author Osman KOCAK
 */
public final class BaseEncoding
{
	/** Base64 encoding scheme. */
	public static final BaseEncoding BASE_64 = new BaseEncoding(Alphabet.BASE_64);

	/** Base64 encoding scheme with URL and filename safe alphabet. */
	public static final BaseEncoding BASE_64_URL = new BaseEncoding(Alphabet.BASE_64_URL);

	/** Base32 encoding scheme. */
	public static final BaseEncoding BASE_32 = new BaseEncoding(Alphabet.BASE_32);

	/** Base32 encoding scheme with extended Hex alphabet. */
	public static final BaseEncoding BASE_32_HEX = new BaseEncoding(Alphabet.BASE_32_HEX);

	/** Base16 encoding scheme. */
	public static final BaseEncoding BASE_16 = new BaseEncoding(Alphabet.BASE_16);

	private static final char PADDING_CHAR = '=';

	private final int n;
	private final String separator;
	private final Alphabet alphabet;
	private final boolean omitPadding;
	private final boolean ignoreUnknownChars;

	private BaseEncoding(Alphabet alphabet)
	{
		this(alphabet, "", -1, !alphabet.requiresPadding(), false);
	}

	private BaseEncoding(Alphabet alphabet, String separator, int n,
		boolean omitPadding, boolean ignoreUnknownChars)
	{
		this.alphabet = alphabet;
		this.omitPadding = omitPadding;
		this.ignoreUnknownChars = ignoreUnknownChars;
		this.separator = separator;
		this.n = n;
	}

	/**
	 * Returns a {@code BaseEncoding} that behaves as this one except that
	 * it adds the specified separator after every {@code n} characters when
	 * encoding data and ignores any occurence of the specified separator
	 * when decoding encoded data.
	 *
	 * See RFC 4648
	 * section 3.1 for more details.
	 *
	 * @param separator the separator to use.
	 * @param n the number of characters between two successive separators.
	 *
	 * @return a {@code BaseEncoding} with the desired configuration.
	 *
	 * @throws NullPointerException if {@code separator} is {@code null}.
	 * @throws IllegalArgumentException if {@code separator} contains any
	 *	alphabet or padding characters or if {@code n <= 0}.
	 */
	public BaseEncoding withSeparator(String separator, int n)
	{
		Parameters.checkCondition(n > 0);
		for (char c : separator.toCharArray()) {
			if (isPaddingChar(c) || isInAlphabet(c)) {
				throw new IllegalArgumentException(
					"Invalid separator character: '" + c + "'");
			}
		}
		return new BaseEncoding(alphabet, separator, n, omitPadding,
			ignoreUnknownChars);
	}

	/**
	 * Returns a {@code BaseEncoding} that behaves as this one except that
	 * it doesn't append padding when encoding data and treats any padding
	 * character as an unknown character when decoding encoded data. Also,
	 * note that since Base16 encoding scheme does not need padding, it is
	 * not affected by this method call.
	 *
	 * See RFC 4648
	 * section 3.2 for more details.
	 *
	 * @return a {@code BaseEncoding} with the desired configuration.
	 */
	public BaseEncoding withoutPadding()
	{
		return new BaseEncoding(alphabet, separator, n, true,
			ignoreUnknownChars);
	}

	/**
	 * Returns a {@code BaseEncoding} that behaves as this one except that
	 * it ignores any unknown character when decoding encoded data.
	 *
	 * See RFC 4648
	 * section 3.3 for more details.
	 *
	 * @return a {@code BaseEncoding} with the desired configuration.
	 */
	public BaseEncoding ignoreUnknownCharacters()
	{
		return new BaseEncoding(alphabet, separator, n, omitPadding, true);
	}

	/**
	 * Encodes the given data bytes according to this {@code BaseEncoding}'s
	 * configuration.
	 *
	 * @param in the data bytes to encode.
	 *
	 * @return the encoded {@code String}.
	 *
	 * @throws NullPointerException if {@code in} is {@code null}.
	 */
	public String encode(byte... in)
	{
		return encode(in, 0, in.length);
	}

	/**
	 * Encodes the given data bytes according to this {@code BaseEncoding}'s
	 * configuration.
	 *
	 * @param in the input buffer.
	 * @param off the input offset.
	 * @param len the number of bytes to encode.
	 *
	 * @return the encoded {@code String}.
	 *
	 * @throws NullPointerException if {@code in} is {@code null}.
	 * @throws IndexOutOfBoundsException if {@code off} or {@code len} is
	 *	negative or if {@code off + len} is greater than {@code in}'s
	 *	length.
	 */
	public String encode(byte[] in, int off, int len)
	{
		checkBounds(in, off, len);
		StringBuilder sb = new StringBuilder(maxEncodedLength(len));
		int accu = 0;
		int count = 0;
		int b = alphabet.bitsPerChar();
		for (int i = off; i < off + len; i++) {
			accu = (accu << 8) | (in[i] & 0xFF);
			count += 8;
			while (count >= b) {
				count -= b;
				sb.append(alphabet.encode(accu >>> count));
			}
		}
		if (count > 0) {
			accu = (accu & (0xFF >>> (8 - count))) << (b - count);
			sb.append(alphabet.encode(accu));
			if (!omitPadding) {
				int c = alphabet.charsPerBlock();
				int pad = c - (sb.length() % c);
				for (int i = 0; i < pad; i++) {
					sb.append(PADDING_CHAR);
				}
			}
		}
		insertSeparators(sb);
		return sb.toString();
	}

	private void checkBounds(byte[] in, int off, int len)
	{
		if (off < 0 || len < 0 || off + len > in.length) {
			throw new IndexOutOfBoundsException();
		}
	}

	private int maxEncodedLength(int l)
	{
		return alphabet.charsPerBlock()
			* (l / alphabet.bytesPerBlock() + 1)
			+ alphabet.maxPaddingLength()
			+ (n > 0 ? (l / n) * separator.length() : 0);
	}

	private void insertSeparators(StringBuilder sb)
	{
		if (n > 0) {
			int step = n + separator.length();
			for (int i = n; i < sb.length(); i += step) {
				sb.insert(i, separator);
			}
		}
	}

	/**
	 * Decodes the given encoded {@code String} according to this
	 * {@code BaseEncoding}'s configuration. Decoding is not case-sensitive
	 * when possible (namely for Base16, Base32 and Base32Hex schemes).
	 *
	 * @param in the input {@code String}.
	 *
	 * @return the decoded data.
	 *
	 * @throws NullPointerException if {@code in} is {@code null}.
	 * @throws IllegalArgumentException if {@code in} is not a valid
	 *	base-encoded {@code String} according to this
	 *	{@code BaseEncoding}'s configuration.
	 */
	public byte[] decode(String in)
	{
		String encoded = n > 0 ? in.replace(separator, "") : in;
		ByteBuffer buf = new ByteBuffer(maxDecodedLength(encoded.length()));
		int accu = 0;
		int count = 0;
		int decoded = 0;
		for (int i = 0; i < encoded.length(); i++) {
			char c = encoded.charAt(i);
			if (isPaddingChar(c) && !omitPadding) {
				break;
			}
			int v = decode(c);
			if (v != -1) {
				decoded++;
				accu = (accu << alphabet.bitsPerChar()) | v;
				count += alphabet.bitsPerChar();
				while (count >= 8) {
					count -= 8;
					buf.append((byte) (accu >>> count));
				}
			}
		}
		if (!omitPadding || !alphabet.requiresPadding()) {
			decoded += getPaddingLength(encoded);
			int blockSize = alphabet.charsPerBlock();
			Parameters.checkCondition(decoded % blockSize == 0);
		}
		return buf.toByteArray();
	}

	private int maxDecodedLength(int l)
	{
		return alphabet.bytesPerBlock()
			* (l / alphabet.charsPerBlock() + 1);
	}

	private boolean isPaddingChar(char c)
	{
		return c == PADDING_CHAR && alphabet.requiresPadding();
	}

	private boolean isInAlphabet(char c)
	{
		return alphabet.decode(c) != -1;
	}

	private int decode(char c)
	{
		int decoded = alphabet.decode(c);
		if (decoded == -1 && !ignoreUnknownChars) {
			throw new IllegalArgumentException(
				"Unknown character: '" + c + "'");
		}
		return decoded;
	}

	private int getPaddingLength(String in)
	{
		int i = in.indexOf(PADDING_CHAR);
		if (i < 0) {
			return 0;
		}
		int count = 1;
		while (++i < in.length()) {
			char c = in.charAt(i);
			if (c == PADDING_CHAR) {
				count++;
			} else if (isInAlphabet(c) || !ignoreUnknownChars) {
				throw new IllegalArgumentException(
					"Invalid padding character: '" + c + "'");
			}
		}
		return count;
	}

	/**
	 * Decodes the specified range from the given encoded {@code String}
	 * according to this {@code BaseEncoding}'s configuration. Decoding is
	 * not case-sensitive when possible (namely for Base16, Base32 and
	 * Base32Hex schemes).
	 *
	 * @param in the input {@code String}.
	 * @param off the input offset.
	 * @param len the number of characters to decode.
	 *
	 * @return the decoded data.
	 *
	 * @throws NullPointerException if {@code in} is {@code null}.
	 * @throws IndexOutOfBoundsException if {@code off} or {@code len} is
	 *	negative or if {@code off + len} is greater than {@code in}'s
	 *	length.
	 * @throws IllegalArgumentException if the specified character range in
	 *	{@code in} is not a valid base-encoded {@code String} according
	 *	to this {@code BaseEncoding}'s configuration.
	 */
	public byte[] decode(String in, int off, int len)
	{
		return decode(in.substring(off, off + len));
	}
}


/*----------------------------------------------------------------------------*
 * This file is part of Pitaya.                                               *
 * Copyright (C) 2012-2016 Osman KOCAK <kocakosm@gmail.com>                   *
 *                                                                            *
 * This program is free software: you can redistribute it and/or modify it    *
 * under the terms of the GNU Lesser General Public License as published by   *
 * the Free Software Foundation, either version 3 of the License, or (at your *
 * option) any later version.                                                 *
 * This program 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 Lesser General Public     *
 * License for more details.                                                  *
 * You should have received a copy of the GNU Lesser General Public License   *
 * along with this program. If not, see <http://www.gnu.org/licenses/>.       *
 *----------------------------------------------------------------------------*/

package org.kocakosm.pitaya.math;

import static java.math.MathContext.DECIMAL128;

import org.kocakosm.pitaya.util.Parameters;

import java.math.BigDecimal;
import java.math.BigInteger;

/**
 * {@link Number} implementation that represents fractions accurately. Instances
 * of this class are immutable.
 *
 * @author Osman KOCAK
 */
public final class Fraction extends Number implements Comparable<Fraction>
{
	private static final long serialVersionUID = 2433311932359200182L;

	/** {@code Fraction} representation of 0. */
	public static final Fraction ZERO = valueOf(0);

	/** {@code Fraction} representation of 1. */
	public static final Fraction ONE = valueOf(1);

	/** {@code Fraction} representation of 1/2. */
	public static final Fraction ONE_HALF = new Fraction(1, 2);

	/** {@code Fraction} representation of 1/3. */
	public static final Fraction ONE_THIRD = new Fraction(1, 3);

	/** {@code Fraction} representation of 2/3. */
	public static final Fraction TWO_THIRDS = new Fraction(2, 3);

	/** {@code Fraction} representation of 1/4. */
	public static final Fraction ONE_QUARTER = new Fraction(1, 4);

	/** {@code Fraction} representation of 2/4. */
	public static final Fraction TWO_QUARTERS = new Fraction(2, 4);

	/** {@code Fraction} representation of 3/4. */
	public static final Fraction THREE_QUARTERS = new Fraction(3, 4);

	/**
	 * Returns the {@code Fraction} representation of the given value.
	 *
	 * @param val an {@code int} value.
	 *
	 * @return the {@code Fraction} representation of the given value.
	 */
	public static Fraction valueOf(int val)
	{
		return new Fraction(BigInteger.valueOf(val), BigInteger.ONE);
	}

	/**
	 * Returns the {@code Fraction} representation of the given value.
	 *
	 * @param val an {@link Integer} value.
	 *
	 * @return the {@code Fraction} representation of the given value.
	 *
	 * @throws NullPointerException if {@code val} is {@code null}.
	 */
	public static Fraction valueOf(Integer val)
	{
		return new Fraction(BigInteger.valueOf(val), BigInteger.ONE);
	}

	/**
	 * Returns the {@code Fraction} representation of the given value.
	 *
	 * @param val a {@link BigInteger} value.
	 *
	 * @return the {@code Fraction} representation of the given value.
	 *
	 * @throws NullPointerException if {@code val} is {@code null}.
	 */
	public static Fraction valueOf(BigInteger val)
	{
		return new Fraction(val, BigInteger.ONE);
	}

	/**
	 * Creates a {@code Fraction} from its {@code String} representation.
	 *
	 * @param str the {@code Fraction}'s {@code String} representation.
	 *
	 * @return the created {@code Fraction}.
	 *
	 * @throws IllegalArgumentException if {@code str} doesn't represent a
	 *	{@code Fraction} or if its denominator equals zero.
	 */
	public static Fraction parse(String str)
	{
		String fraction = str.replaceAll("\\s", "");
		int index = fraction.indexOf('/');
		if (index < 0) {
			return valueOf(new BigInteger(fraction));
		}
		BigInteger n = new BigInteger(fraction.substring(0, index));
		BigInteger d = new BigInteger(fraction.substring(index + 1));
		return new Fraction(n, d);
	}

	private final BigInteger n;
	private final BigInteger d;
	private final BigDecimal v;

	/**
	 * Creates a new {@code Fraction}.
	 *
	 * @param numerator the numerator.
	 * @param denominator the denominator.
	 *
	 * @throws IllegalArgumentException if {@code denominator} equals zero.
	 */
	public Fraction(int numerator, int denominator)
	{
		this(BigInteger.valueOf(numerator),
			BigInteger.valueOf(denominator));
	}

	/**
	 * Creates a new {@code Fraction}.
	 *
	 * @param numerator the numerator.
	 * @param denominator the denominator.
	 *
	 * @throws NullPointerException if one of the arguments is {@code null}.
	 * @throws IllegalArgumentException if {@code denominator} equals zero.
	 */
	public Fraction(BigInteger numerator, BigInteger denominator)
	{
		Parameters.checkCondition(!BigInteger.ZERO.equals(denominator));
		n = numerator;
		d = denominator;
		v = new BigDecimal(n).divide(new BigDecimal(d), DECIMAL128);
	}

	/**
	 * Adds the given value to this one and returns the result in reduced
	 * form.
	 *
	 * @param f the fraction to add to this one.
	 *
	 * @return {@code this + f} in reduced form.
	 *
	 * @throws NullPointerException if {@code f} is {@code null}.
	 */
	public Fraction plus(Fraction f)
	{
		return new Fraction(n.multiply(f.d).add(f.n.multiply(d)),
			d.multiply(f.d)).reduced();
	}

	/**
	 * Subtracts the given value from this one and returns the result in
	 * reduced form.
	 *
	 * @param f the fraction to subtract from this one.
	 *
	 * @return {@code this - f} in reduced form.
	 *
	 * @throws NullPointerException if {@code f} is {@code null}.
	 */
	public Fraction minus(Fraction f)
	{
		return new Fraction(n.multiply(f.d).subtract(f.n.multiply(d)),
			d.multiply(f.d)).reduced();
	}

	/**
	 * Multiplies this value by the given one and returns the result in
	 * reduced form.
	 *
	 * @param f the fraction to multiply this one with.
	 *
	 * @return {@code this * f} in reduced form.
	 *
	 * @throws NullPointerException if {@code f} is {@code null}.
	 */
	public Fraction multipliedBy(Fraction f)
	{
		return new Fraction(n.multiply(f.n), d.multiply(f.d)).reduced();
	}

	/**
	 * Divides this value by the given one and returns the result in reduced
	 * form.
	 *
	 * @param f the fraction to divide this one with.
	 *
	 * @return {@code this / f} in reduced form.
	 *
	 * @throws NullPointerException if {@code f} is {@code null}.
	 * @throws ArithmeticException if {@code f} equals zero.
	 */
	public Fraction dividedBy(Fraction f)
	{
		if (ZERO.equals(f)) {
			throw new ArithmeticException("Division by zero");
		}
		return new Fraction(n.multiply(f.d), d.multiply(f.n)).reduced();
	}

	/**
	 * Returns the {@code Fraction} obtained by raising this to the given
	 * power, in reduced form.
	 *
	 * @param exponent the power to raise this fraction to.
	 *
	 * @return {@code this ^ exponent} in reduced form.
	 *
	 * @throws ArithmeticException if {@code exponent} is negative.
	 */
	public Fraction power(int exponent)
	{
		return new Fraction(n.pow(exponent), d.pow(exponent)).reduced();
	}

	/**
	 * Returns the sign of this {@code Fraction}, that is, this method
	 * returns {@code 1} if this fraction is positive, {@code 0} if it is
	 * equal to {@code 0}, and {@code -1} if it is negative.
	 *
	 * @return the sign of this {@code Fraction}.
	 */
	public int sign()
	{
		return n.signum() * d.signum();
	}

	/**
	 * Returns a {@code Fraction} which is the absolute value of this one.
	 *
	 * @return the absolute value of this {@code Fraction}.
	 */
	public Fraction absoluteValue()
	{
		return new Fraction(n.abs(), d.abs());
	}

	/**
	 * Returns the opposite of this {@code Fraction}. The result is not
	 * reduced before being returned.
	 *
	 * @return {@code -this}.
	 */
	public Fraction negated()
	{
		if (d.signum() < 0) {
			return new Fraction(n, d.negate());
		}
		return new Fraction(n.negate(), d);
	}

	/**
	 * Returns the inverse of this {@code Fraction}. The result is not
	 * reduced before being returned.
	 *
	 * @return {@code 1 / this}.
	 */
	public Fraction inverted()
	{
		return new Fraction(d, n);
	}

	/**
	 * Returns the reduced form of this {@code Fraction}. Also, note that
	 * the sign information will be held by the numerator.
	 *
	 * @return the reduced form of this {@code Fraction}.
	 */
	public Fraction reduced()
	{
		BigInteger gcd = n.gcd(d);
		if (d.signum() < 0) {
			BigInteger numerator = n.divide(gcd).negate();
			BigInteger denominator = d.divide(gcd).negate();
			return new Fraction(numerator, denominator);
		}
		return new Fraction(n.divide(gcd), d.divide(gcd));
	}

	/**
	 * Returns this {@code Fraction}'s numerator.
	 *
	 * @return this {@code Fraction}'s numerator.
	 */
	public BigInteger numerator()
	{
		return n;
	}

	/**
	 * Returns this {@code Fraction}'s denominator.
	 *
	 * @return this {@code Fraction}'s denominator.
	 */
	public BigInteger denominator()
	{
		return d;
	}

	/**
	 * Returns the value of this {@code Fraction} as a {@code BigDecimal},
	 * rounded in accordance to IEEE 754R Decimal128 format.
	 *
	 * @return the value of this {@code Fraction} as a {@code BigDecimal}.
	 */
	public BigDecimal bigDecimalValue()
	{
		return v;
	}

	@Override
	public int intValue()
	{
		return v.intValue();
	}

	@Override
	public long longValue()
	{
		return v.longValue();
	}

	@Override
	public float floatValue()
	{
		return v.floatValue();
	}

	@Override
	public double doubleValue()
	{
		return v.doubleValue();
	}

	@Override
	public int compareTo(Fraction f)
	{
		return n.multiply(f.d).compareTo(f.n.multiply(d));
	}

	@Override
	public boolean equals(Object o)
	{
		if (o == this) {
			return true;
		}
		if (!(o instanceof Fraction)) {
			return false;
		}
		Fraction f1 = reduced();
		Fraction f2 = ((Fraction) o).reduced();
		return f1.n.equals(f2.n) && f1.d.equals(f2.d);
	}

	@Override
	public int hashCode()
	{
		Fraction f = reduced();
		int hash = 7;
		hash = 71 * hash + f.n.hashCode();
		hash = 71 * hash + f.d.hashCode();
		return hash;
	}

	@Override
	public String toString()
	{
		return n + " / " + d;
	}
}