All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.irurueta.sorting.Sorter Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (C) 2012 Alberto Irurueta Carro ([email protected])
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.irurueta.sorting;

import java.util.Comparator;

/**
 * Sorts instances of type T in provided arrays using any of the
 * available methods.
 *
 * @param  Type of instances being sorted.
 */
@SuppressWarnings({"WeakerAccess", "Duplicates"})
public abstract class Sorter {

    /**
     * Default method to be used for sorting if none is provided.
     */
    public static final SortingMethod DEFAULT_SORTING_METHOD = SortingMethod.SYSTEM_SORTING_METHOD;

    /**
     * Sorts provided array in ascending order so that {@code
     * array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     *
     * @param array      Array to be sorted. After execution of this method
     *                   elements in array between fromIndex (inclusive) and toIndex
     *                   (exclusive) are modified so that they are on ascending order.
     * @param fromIndex  Index were sorting starts (inclusive).
     * @param toIndex    Index were sorting stops (exclusive).
     * @param comparator Determines whether an element is greater or lower
     *                   than another one.
     * @throws SortingException               If for some reason sorting fails.
     * @throws IllegalArgumentException       If {@code fromIndex > toIndex}.
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *                                        {@code toIndex > array.length}.
     */
    public abstract void sort(final T[] array, final int fromIndex, final int toIndex, final Comparator comparator)
            throws SortingException;

    /**
     * Sorts provided array in ascending order so that {@code
     * array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     * An array containing the original indices where elements were
     * located is returned so that other arrays or collections can be kept
     * in the same order.
     *
     * @param array      Array to be sorted. After execution of this method
     *                   elements in array between fromIndex (inclusive) and toIndex
     *                   (exclusive) are modified so that they are on ascending order.
     * @param fromIndex  Index were sorting starts (inclusive).
     * @param toIndex    Index were sorting stops (exclusive).
     * @param comparator Determines whether an element is greater or lower
     *                   than another one.
     * @return Array containing original location of elements that have been
     * sorted. Only elements between fromIndex (inclusive) and toIndex
     * (exclusive) are modified, the remaining ones are kept in natural
     * order.
     * @throws SortingException               If for some reason sorting fails.
     * @throws IllegalArgumentException       If {@code fromIndex > toIndex}.
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *                                        {@code toIndex > array.length}.
     */
    public abstract int[] sortWithIndices(final T[] array, final int fromIndex, final int toIndex,
                                          final Comparator comparator) throws SortingException;

    /**
     * Sorts provided array in ascending order so that {@code
     * array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     *
     * @param array     Array to be sorted. After execution of this method
     *                  elements in array between fromIndex (inclusive) and toIndex
     *                  (exclusive) are modified so that they are on ascending order.
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @throws SortingException               If for some reason sorting fails.
     * @throws IllegalArgumentException       If {@code fromIndex > toIndex}.
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *                                        {@code toIndex > array.length}.
     */
    public abstract void sort(final double[] array, final int fromIndex, final int toIndex)
            throws SortingException;

    /**
     * Sorts provided array in ascending order so that {@code
     * array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     * An array containing the original indices where elements were
     * located is returned so that other arrays or collections can be kept
     * in the same order.
     *
     * @param array     Array to be sorted. After execution of this method
     *                  elements in array between fromIndex (inclusive) and toIndex
     *                  (exclusive) are modified so that they are on ascending order.
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @return Array containing original location of elements that have been
     * sorted. Only elements between fromIndex (inclusive) and toIndex
     * (exclusive) are modified, the remaining ones are kept in natural
     * order.
     * @throws SortingException               If for some reason sorting fails.
     * @throws IllegalArgumentException       If {@code fromIndex > toIndex}.
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *                                        {@code toIndex > array.length}.
     */
    public abstract int[] sortWithIndices(final double[] array, final int fromIndex, final int toIndex)
            throws SortingException;

    /**
     * Sorts provided array in ascending order so that {@code
     * array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     *
     * @param array     Array to be sorted. After execution of this method
     *                  elements in array between fromIndex (inclusive) and toIndex
     *                  (exclusive) are modified so that they are on ascending order.
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @throws SortingException               If for some reason sorting fails.
     * @throws IllegalArgumentException       If {@code fromIndex > toIndex}
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *                                        {@code toIndex > array.length}.
     */
    public abstract void sort(final float[] array, final int fromIndex, final int toIndex) throws SortingException;

    /**
     * Sorts provided array in ascending order so that {@code
     * array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     * An array containing the original indices where elements were
     * located is returned so that other arrays or collections can be kept
     * in the same order.
     *
     * @param array     Array to be sorted. After execution of this method
     *                  elements in array between fromIndex (inclusive) and toIndex
     *                  (exclusive) are modified so that they are on ascending order.
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @return Array containing original location of elements that have been
     * sorted. Only elements between fromIndex (inclusive) and toIndex
     * (exclusive) are modified, the remaining ones are kept in natural
     * order.
     * @throws SortingException               If for some reason sorting fails.
     * @throws IllegalArgumentException       If {@code fromIndex > toIndex}.
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *                                        {@code toIndex > array.length}.
     */
    public abstract int[] sortWithIndices(final float[] array, final int fromIndex, final int toIndex)
            throws SortingException;

    /**
     * Sorts provided array in ascending order so that {@code
     * array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     *
     * @param array     Array to be sorted. After execution of this method
     *                  elements in array between fromIndex (inclusive) and toIndex
     *                  (exclusive) are modified so that they are on ascending order.
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @throws SortingException               If for some reason sorting fails.
     * @throws IllegalArgumentException       If {@code fromIndex > toIndex}.
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *                                        {@code toIndex > array.length}.
     */
    public abstract void sort(final int[] array, final int fromIndex, final int toIndex) throws SortingException;

    /**
     * Sorts provided array in ascending order so that {@code
     * array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     * An array containing the original indices where elements were
     * located is returned so that other arrays or collections can be kept
     * in the same order.
     *
     * @param array     Array to be sorted. After execution of this method
     *                  elements in array between fromIndex (inclusive) and toIndex
     *                  (exclusive) are modified so that they are on ascending order.
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @return Array containing original location of elements that have been
     * sorted. Only elements between fromIndex (inclusive) and toIndex
     * (exclusive) are modified, the remaining ones are kept in natural
     * order.
     * @throws SortingException               If for some reason sorting fails.
     * @throws IllegalArgumentException       If {@code fromIndex > toIndex}.
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *                                        {@code toIndex > array.length}.
     */
    public abstract int[] sortWithIndices(final int[] array, final int fromIndex, final int toIndex)
            throws SortingException;

    /**
     * Sorts provided array in ascending order so that {@code
     * array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     *
     * @param array     Array to be sorted. After execution of this method
     *                  elements in array between fromIndex (inclusive) and toIndex
     *                  (exclusive) are modified so that they are on ascending order.
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @throws SortingException               If for some reason sorting fails.
     * @throws IllegalArgumentException       If {@code fromIndex > toIndex}.
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *                                        {@code toIndex > array.length}.
     */
    public abstract void sort(final long[] array, final int fromIndex, final int toIndex) throws SortingException;

    /**
     * Sorts provided array in ascending order so that {@code
     * array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     * An array containing the original indices where elements were
     * located is returned so that other arrays or collections can be kept
     * in the same order.
     *
     * @param array     Array to be sorted. After execution of this method
     *                  elements in array between fromIndex (inclusive) and toIndex
     *                  (exclusive) are modified so that they are on ascending order.
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @return Array containing original location of elements that have been
     * sorted. Only elements between fromIndex (inclusive) and toIndex
     * (exclusive) are modified, the remaining ones are kept in natural
     * order.
     * @throws SortingException               If for some reason sorting fails.
     * @throws IllegalArgumentException       If {@code fromIndex > toIndex}.
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *                                        {@code toIndex > array.length}.
     */
    public abstract int[] sortWithIndices(final long[] array, final int fromIndex, final int toIndex)
            throws SortingException;

    /**
     * Sorts provided array of {@link Comparable} in ascending order so that
     * {@code array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     *
     * @param array     Array to be sorted. After execution of this method
     *                  elements in array between fromIndex (inclusive) and toIndex
     *                  (exclusive) are modified so that they are on ascending order.
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @throws SortingException               If for some reason sorting fails.
     * @throws IllegalArgumentException       If {@code fromIndex > toIndex}.
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *                                        {@code toIndex > array.length}.
     */
    @SuppressWarnings("unchecked")
    public void sort(final Comparable[] array, final int fromIndex, final int toIndex) throws SortingException {
        sort((T[]) array, fromIndex, toIndex, (t1, t2) -> {
            final var t1b = (Comparable) t1;
            return t1b.compareTo(t2);
        });
    }

    /**
     * Sorts provided array of {@link Comparable} in ascending order so that
     * {@code array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     *
     * @param array Array to be sorted. After execution of this method
     *              all elements in array are modified so that they are on ascending
     *              order.
     * @throws SortingException If for some reason sorting fails.
     */
    public void sort(final Comparable[] array) throws SortingException {
        sort(array, 0, array.length);
    }

    /**
     * Sorts provided array in ascending order so that {@code
     * array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     *
     * @param array      Array to be sorted. After execution of this method
     *                   all elements in array are modified so that they are on ascending
     *                   order.
     * @param comparator Determines whether an element is greater or lower
     *                   than another one.
     * @throws SortingException               If for some reason sorting fails.
     * @throws IllegalArgumentException       If {@code fromIndex > toIndex}.
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *                                        {@code toIndex > array.length}.
     */
    public void sort(final T[] array, final Comparator comparator) throws SortingException {
        sort(array, 0, array.length, comparator);
    }

    /**
     * Sorts provided array in ascending order so that {@code
     * array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     *
     * @param array Array to be sorted. After execution of this method
     *              all elements in array are modified so that they are on ascending
     *              order.
     * @throws SortingException               If for some reason sorting fails.
     * @throws IllegalArgumentException       If {@code fromIndex > toIndex}.
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *                                        {@code toIndex > array.length}.
     */
    public void sort(final double[] array) throws SortingException {
        sort(array, 0, array.length);
    }

    /**
     * Sorts provided array in ascending order so that {@code
     * array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     *
     * @param array Array to be sorted. After execution of this method
     *              all elements in array are modified so that they are on ascending
     *              order.
     * @throws SortingException               If for some reason sorting fails.
     * @throws IllegalArgumentException       If {@code fromIndex > toIndex}.
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *                                        {@code toIndex > array.length}.
     */
    public void sort(final float[] array) throws SortingException {
        sort(array, 0, array.length);
    }

    /**
     * Sorts provided array in ascending order so that {@code
     * array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     *
     * @param array Array to be sorted. After execution of this method
     *              all elements in array are modified so that they are on ascending
     *              order.
     * @throws SortingException               If for some reason sorting fails.
     * @throws IllegalArgumentException       If {@code fromIndex > toIndex}.
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *                                        {@code toIndex > array.length}.
     */
    public void sort(final int[] array) throws SortingException {
        sort(array, 0, array.length);
    }

    /**
     * Sorts provided array in ascending order so that {@code
     * array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     *
     * @param array Array to be sorted. After execution of this method
     *              all elements in array are modified so that they are on ascending
     *              order.
     * @throws SortingException               If for some reason sorting fails.
     * @throws IllegalArgumentException       If {@code fromIndex > toIndex}.
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *                                        {@code toIndex > array.length}.
     */
    public void sort(final long[] array) throws SortingException {
        sort(array, 0, array.length);
    }


    /**
     * Sorts provided array of {@link Comparable} in ascending order so that
     * {@code array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     * An array containing the original indices where elements were
     * located is returned so that other arrays or collections can be kept
     * in the same order.
     *
     * @param array     Array to be sorted. After execution of this method
     *                  elements in array between fromIndex (inclusive) and toIndex
     *                  (exclusive) are modified so that they are on ascending order.
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @return Array containing original location of elements that have been
     * sorted. Only elements between fromIndex (inclusive) and toIndex
     * (exclusive) are modified, the remaining ones are kept in natural
     * order.
     * @throws SortingException               If for some reason sorting fails.
     * @throws IllegalArgumentException       If {@code fromIndex > toIndex}.
     * @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or
     *                                        {@code toIndex > array.length}.
     */
    @SuppressWarnings("unchecked")
    public int[] sortWithIndices(final Comparable[] array, final int fromIndex, final int toIndex)
            throws SortingException {
        return sortWithIndices((T[]) array, fromIndex, toIndex, (t1, t2) -> {
            final var t1b = (Comparable) t1;
            return t1b.compareTo(t2);
        });
    }

    /**
     * Sorts provided array of {@link Comparable} in ascending order so that
     * {@code array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     * An array containing the original indices where elements were
     * located is returned so that other arrays or collections can be kept
     * in the same order.
     *
     * @param array Array to be sorted. After execution of this method
     *              all elements in array are modified so that they are on ascending
     *              order.
     * @return Array containing original location of elements that have been
     * sorted.
     * @throws SortingException If for some reason sorting fails.
     */
    public int[] sortWithIndices(final Comparable[] array) throws SortingException {
        return sortWithIndices(array, 0, array.length);
    }

    /**
     * Sorts provided array of {@link Comparable} in ascending order so that
     * {@code array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     * An array containing the original indices where elements were
     * located is returned so that other arrays or collections can be kept
     * in the same order.
     *
     * @param array      Array to be sorted. After execution of this method
     *                   all elements in array are modified so that they are on ascending
     *                   order.
     * @param comparator Determines whether an element is greater or lower
     *                   than another one.
     * @return Array containing original location of elements that have been
     * sorted.
     * @throws SortingException If for some reason sorting fails.
     */
    public int[] sortWithIndices(final T[] array, final Comparator comparator) throws SortingException {
        return sortWithIndices(array, 0, array.length, comparator);
    }

    /**
     * Sorts provided array of {@link Comparable} in ascending order so that
     * {@code array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     * An array containing the original indices where elements were
     * located is returned so that other arrays or collections can be kept
     * in the same order.
     *
     * @param array Array to be sorted. After execution of this method
     *              all elements in array are modified so that they are on ascending
     *              order.
     * @return Array containing original location of elements that have been
     * sorted.
     * @throws SortingException If for some reason sorting fails.
     */
    public int[] sortWithIndices(final double[] array) throws SortingException {
        return sortWithIndices(array, 0, array.length);
    }

    /**
     * Sorts provided array of {@link Comparable} in ascending order so that
     * {@code array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     * An array containing the original indices where elements were
     * located is returned so that other arrays or collections can be kept
     * in the same order.
     *
     * @param array Array to be sorted. After execution of this method
     *              all elements in array are modified so that they are on ascending
     *              order.
     * @return Array containing original location of elements that have been
     * sorted.
     * @throws SortingException If for some reason sorting fails.
     */
    public int[] sortWithIndices(final float[] array) throws SortingException {
        return sortWithIndices(array, 0, array.length);
    }

    /**
     * Sorts provided array of {@link Comparable} in ascending order so that
     * {@code array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     * An array containing the original indices where elements were
     * located is returned so that other arrays or collections can be kept
     * in the same order.
     *
     * @param array Array to be sorted. After execution of this method
     *              all elements in array are modified so that they are on ascending
     *              order.
     * @return Array containing original location of elements that have been
     * sorted.
     * @throws SortingException If for some reason sorting fails.
     */
    public int[] sortWithIndices(final int[] array) throws SortingException {
        return sortWithIndices(array, 0, array.length);
    }

    /**
     * Sorts provided array of {@link Comparable} in ascending order so that
     * {@code array[i - 1] < array[i]} for any valid i.
     * This method modifies provided array so that
     * after execution of this method array elements are ordered.
     * An array containing the original indices where elements were
     * located is returned so that other arrays or collections can be kept
     * in the same order.
     *
     * @param array Array to be sorted. After execution of this method
     *              all elements in array are modified so that they are on ascending
     *              order.
     * @return Array containing original location of elements that have been
     * sorted.
     * @throws SortingException If for some reason sorting fails.
     */
    public int[] sortWithIndices(final long[] array) throws SortingException {
        return sortWithIndices(array, 0, array.length);
    }

    /**
     * Returns the k-th sorted element in provided array of {@link Comparable}.
     * Selecting an element is usually faster than sorting the whole
     * array, and for that reason, when only a few sorted elements are
     * required, this method should be used instead of sort.
     * Because array is passed by reference, after executing this method
     * array is modified so that in k location it contains the k-th sorted
     * elements and on locations array[0] ... array[k-1] contains unsorted
     * elements smaller than sorted element array[k],  and on locations
     * array[k+1] ... array[length-1] contains unsorted elements greater
     * than sorted element array[k].
     *
     * @param k     Position of sorted element to be retrieved.
     * @param array Array to be used for retrieving k-th sorted element.
     *              Provided array is passed by reference and modified upon execution of
     *              this method so that k-th location contains k-th sorted element, and
     *              array[0] ... array[k-1] contains unsorted elements smaller than
     *              sorted element array[k] and array[k+1] ... array[length-1] contains
     *              elements greater than sorted element array[k]
     * @return The k-th sorted element in provided array
     * @throws IllegalArgumentException if k < array.length
     */
    public T select(final int k, final Comparable[] array) {
        return select(k, array, 0, array.length);
    }

    /**
     * Returns the k-th sorted element in provided array of {@link Comparable}
     * starting at fromIndex and finishing at toIndex, elements outside this
     * range are ignored.
     * Selecting an element is usually faster than sorting the whole
     * array, and for that reason, when only a few sorted elements are
     * required, this method should be used instead of sort.
     * Because array is passed by reference, after executing this method
     * array is modified so that in k location it contains the k-th sorted
     * elements and on locations array[fromIndex] ... array[k-1 + fromIndex]
     * contains unsorted elements smaller than sorted element
     * array[k + fromIndex],  and on locations
     * array[k+1 + fromIndex] ... array[toIndex-1] contains unsorted
     * elements greater than sorted element array[k + fromIndex].
     *
     * @param k         Position of sorted element to be retrieved.
     * @param array     Array to be used for retrieving k-th sorted element.
     *                  Provided array is passed by reference and modified upon execution of
     *                  this method so that k-th location contains k-th sorted element, and
     *                  array[fromIndex] ... array[k-1 + fromIndex] contains unsorted
     *                  elements smaller than sorted element array[k + fromIndex] and
     *                  array[k+1 + fromIndex] ... array[toIndex-1] contains elements
     *                  greater than sorted element array[k].
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @return The k-th sorted element in provided array.
     * @throws IllegalArgumentException       if k < (toIndex - fromIndex) or
     *                                        fromIndex < toIndex.
     * @throws ArrayIndexOutOfBoundsException if fromIndex or toIndex are
     *                                        outside array boundaries.
     */
    @SuppressWarnings("unchecked")
    public T select(final int k, final Comparable[] array, final int fromIndex, final int toIndex) {
        return select(k, (T[]) array, fromIndex, toIndex, (t1, t2) -> {
            final var t1b = (Comparable) t1;
            return t1b.compareTo(t2);
        });
    }

    /**
     * Returns the k-th sorted element in provided array.
     * Selecting an element is usually faster than sorting the whole
     * array, and for that reason, when only a few sorted elements are
     * required, this method should be used instead of sort.
     * Because array is passed by reference, after executing this method
     * array is modified so that in k location it contains the k-th sorted
     * elements and on locations array[0] ... array[k-1] contains unsorted
     * elements smaller than sorted element array[k],  and on locations
     * array[k+1] ... array[length-1] contains unsorted elements greater
     * than sorted element array[k].
     *
     * @param k          Position of sorted element to be retrieved.
     * @param array      Array to be used for retrieving k-th sorted element.
     *                   Provided array is passed by reference and modified upon execution of
     *                   this method so that k-th location contains k-th sorted element, and
     *                   array[0] ... array[k-1] contains unsorted elements smaller than
     *                   sorted element array[k] and array[k+1] ... array[length-1] contains
     *                   elements greater than sorted element array[k].
     * @param comparator Determines whether an element is greater or lower
     *                   than another one.
     * @return The k-th sorted element in provided array.
     * @throws IllegalArgumentException k < array.length.
     */
    public T select(final int k, final T[] array, final Comparator comparator) {
        return select(k, array, 0, array.length, comparator);
    }

    /**
     * Returns the k-th sorted element in provided array.
     * Selecting an element is usually faster than sorting the whole
     * array, and for that reason, when only a few sorted elements are
     * required, this method should be used instead of sort.
     * Because array is passed by reference, after executing this method
     * array is modified so that in k location it contains the k-th sorted
     * elements and on locations array[0] ... array[k-1] contains unsorted
     * elements smaller than sorted element array[k],  and on locations
     * array[k+1] ... array[length-1] contains unsorted elements greater
     * than sorted element array[k].
     *
     * @param k     Position of sorted element to be retrieved.
     * @param array Array to be used for retrieving k-th sorted element.
     *              Provided array is passed by reference and modified upon execution of
     *              this method so that k-th location contains k-th sorted element, and
     *              array[0] ... array[k-1] contains unsorted elements smaller than
     *              sorted element array[k] and array[k+1] ... array[length-1] contains
     *              elements greater than sorted element array[k].
     * @return The k-th sorted element in provided array.
     * @throws IllegalArgumentException k < array.length.
     */
    public double select(final int k, final double[] array) {
        return select(k, array, 0, array.length);
    }

    /**
     * Returns the k-th sorted element in provided array.
     * Selecting an element is usually faster than sorting the whole
     * array, and for that reason, when only a few sorted elements are
     * required, this method should be used instead of sort.
     * Because array is passed by reference, after executing this method
     * array is modified so that in k location it contains the k-th sorted
     * elements and on locations array[0] ... array[k-1] contains unsorted
     * elements smaller than sorted element array[k],  and on locations
     * array[k+1] ... array[length-1] contains unsorted elements greater
     * than sorted element array[k].
     *
     * @param k     Position of sorted element to be retrieved.
     * @param array Array to be used for retrieving k-th sorted element.
     *              Provided array is passed by reference and modified upon execution of
     *              this method so that k-th location contains k-th sorted element, and
     *              array[0] ... array[k-1] contains unsorted elements smaller than
     *              sorted element array[k] and array[k+1] ... array[length-1] contains
     *              elements greater than sorted element array[k].
     * @return The k-th sorted element in provided array.
     * @throws IllegalArgumentException k < array.length.
     */
    public float select(final int k, final float[] array) {
        return select(k, array, 0, array.length);
    }

    /**
     * Returns the k-th sorted element in provided array.
     * Selecting an element is usually faster than sorting the whole
     * array, and for that reason, when only a few sorted elements are
     * required, this method should be used instead of sort.
     * Because array is passed by reference, after executing this method
     * array is modified so that in k location it contains the k-th sorted
     * elements and on locations array[0] ... array[k-1] contains unsorted
     * elements smaller than sorted element array[k],  and on locations
     * array[k+1] ... array[length-1] contains unsorted elements greater
     * than sorted element array[k].
     *
     * @param k     Position of sorted element to be retrieved.
     * @param array Array to be used for retrieving k-th sorted element.
     *              Provided array is passed by reference and modified upon execution of
     *              this method so that k-th location contains k-th sorted element, and
     *              array[0] ... array[k-1] contains unsorted elements smaller than
     *              sorted element array[k] and array[k+1] ... array[length-1] contains
     *              elements greater than sorted element array[k].
     * @return The k-th sorted element in provided array.
     * @throws IllegalArgumentException k < array.length.
     */
    public int select(final int k, final int[] array) {
        return select(k, array, 0, array.length);
    }

    /**
     * Returns the k-th sorted element in provided array.
     * Selecting an element is usually faster than sorting the whole
     * array, and for that reason, when only a few sorted elements are
     * required, this method should be used instead of sort.
     * Because array is passed by reference, after executing this method
     * array is modified so that in k location it contains the k-th sorted
     * elements and on locations array[0] ... array[k-1] contains unsorted
     * elements smaller than sorted element array[k],  and on locations
     * array[k+1] ... array[length-1] contains unsorted elements greater
     * than sorted element array[k].
     *
     * @param k     Position of sorted element to be retrieved.
     * @param array Array to be used for retrieving k-th sorted element.
     *              Provided array is passed by reference and modified upon execution of
     *              this method so that k-th location contains k-th sorted element, and
     *              array[0] ... array[k-1] contains unsorted elements smaller than
     *              sorted element array[k] and array[k+1] ... array[length-1] contains
     *              elements greater than sorted element array[k].
     * @return The k-th sorted element in provided array.
     * @throws IllegalArgumentException k < array.length.
     */
    public long select(final int k, final long[] array) {
        return select(k, array, 0, array.length);
    }

    /**
     * Returns the k-th sorted element in provided array of {@link Comparable}
     * starting at fromIndex and finishing at toIndex, elements outside this
     * range are ignored.
     * Selecting an element is usually faster than sorting the whole
     * array, and for that reason, when only a few sorted elements are
     * required, this method should be used instead of sort.
     * Because array is passed by reference, after executing this method
     * array is modified so that in k location it contains the k-th sorted
     * elements and on locations array[fromIndex] ... array[k-1 + fromIndex]
     * contains unsorted elements smaller than sorted element
     * array[k + fromIndex],  and on locations
     * array[k+1 + fromIndex] ... array[toIndex-1] contains unsorted
     * elements greater than sorted element array[k + fromIndex]
     *
     * @param k          Position of sorted element to be retrieved.
     * @param array      Array to be used for retrieving k-th sorted element.
     *                   Provided array is passed by reference and modified upon execution of
     *                   this method so that k-th location contains k-th sorted element, and
     *                   array[fromIndex] ... array[k-1 + fromIndex] contains unsorted
     *                   elements smaller than sorted element array[k + fromIndex] and
     *                   array[k+1 + fromIndex] ... array[toIndex-1] contains elements
     *                   greater than sorted element array[k].
     * @param comparator Determines whether an element is greater or lower
     *                   than another one.
     * @param fromIndex  Index were sorting starts (inclusive).
     * @param toIndex    Index were sorting stops (exclusive).
     * @return The k-th sorted element in provided array.
     * @throws IllegalArgumentException       if k < (toIndex - fromIndex) or
     *                                        fromIndex < toIndex.
     * @throws ArrayIndexOutOfBoundsException if fromIndex or toIndex are
     *                                        outside array boundaries.
     */
    public T select(final int k, final T[] array, final int fromIndex, final int toIndex,
                    final Comparator comparator) {

        if (fromIndex > toIndex) {
            throw new IllegalArgumentException();
        }
        if (fromIndex < 0 || toIndex > array.length) {
            throw new ArrayIndexOutOfBoundsException();
        }

        int i;
        int ir;
        int j;
        int l;
        int mid;
        final var n = toIndex - fromIndex;
        if (k >= n) {
            throw new IllegalArgumentException();
        }

        T a;
        l = 0;
        ir = n - 1;
        for (; ; ) {
            if (ir <= l + 1) {
                if (ir == l + 1 && comparator.compare(array[ir + fromIndex], array[l + fromIndex]) < 0) {
                    swap(array, l + fromIndex, ir + fromIndex);
                }
                return array[k + fromIndex];
            } else {
                mid = (l + ir) >> 1;
                swap(array, mid + fromIndex, l + 1 + fromIndex);
                if (comparator.compare(array[l + fromIndex], array[ir + fromIndex]) > 0) {
                    swap(array, l + fromIndex, ir + fromIndex);
                }
                if (comparator.compare(array[l + 1 + fromIndex], array[ir + fromIndex]) > 0) {
                    swap(array, l + 1 + fromIndex, ir + fromIndex);
                }
                if (comparator.compare(array[l + fromIndex], array[l + 1 + fromIndex]) > 0) {
                    swap(array, l + fromIndex, l + 1 + fromIndex);
                }
                i = l + 1;
                j = ir;
                a = array[l + 1 + fromIndex];
                for (; ; ) {
                    do {
                        i++;
                    } while (comparator.compare(array[i + fromIndex], a) < 0);

                    do {
                        j--;
                    } while (comparator.compare(array[j + fromIndex], a) > 0);

                    if (j < i) {
                        break;
                    }

                    swap(array, i + fromIndex, j + fromIndex);
                }
                array[l + 1 + fromIndex] = array[j + fromIndex];
                array[j + fromIndex] = a;
                if (j >= k) {
                    ir = j - 1;
                }
                if (j <= k) {
                    l = i;
                }
            }
        }
    }

    /**
     * Returns the k-th sorted element in provided array of {@link Comparable}
     * starting at fromIndex and finishing at toIndex, elements outside this
     * range are ignored.
     * Selecting an element is usually faster than sorting the whole
     * array, and for that reason, when only a few sorted elements are
     * required, this method should be used instead of sort.
     * Because array is passed by reference, after executing this method
     * array is modified so that in k location it contains the k-th sorted
     * elements and on locations array[fromIndex] ... array[k-1 + fromIndex]
     * contains unsorted elements smaller than sorted element
     * array[k + fromIndex],  and on locations
     * array[k+1 + fromIndex] ... array[toIndex-1] contains unsorted
     * elements greater than sorted element array[k + fromIndex].
     *
     * @param k         Position of sorted element to be retrieved.
     * @param array     Array to be used for retrieving k-th sorted element.
     *                  Provided array is passed by reference and modified upon execution of
     *                  this method so that k-th location contains k-th sorted element, and
     *                  array[fromIndex] ... array[k-1 + fromIndex] contains unsorted
     *                  elements smaller than sorted element array[k + fromIndex] and
     *                  array[k+1 + fromIndex] ... array[toIndex-1] contains elements
     *                  greater than sorted element array[k].
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @return The k-th sorted element in provided array.
     * @throws IllegalArgumentException       if k < (toIndex - fromIndex) or
     *                                        fromIndex < toIndex.
     * @throws ArrayIndexOutOfBoundsException if fromIndex or toIndex are
     *                                        outside array boundaries.
     */
    public double select(final int k, final double[] array, final int fromIndex, final int toIndex) {

        if (fromIndex > toIndex) {
            throw new IllegalArgumentException();
        }
        if (fromIndex < 0 || toIndex > array.length) {
            throw new ArrayIndexOutOfBoundsException();
        }

        int i;
        int ir;
        int j;
        int l;
        int mid;
        final var n = toIndex - fromIndex;
        if (k >= n) {
            throw new IllegalArgumentException();
        }

        double a;
        l = 0;
        ir = n - 1;
        for (; ; ) {
            if (ir <= l + 1) {
                if (ir == l + 1 && array[ir + fromIndex] < array[l + fromIndex]) {
                    swap(array, l + fromIndex, ir + fromIndex);
                }
                return array[k + fromIndex];
            } else {
                mid = (l + ir) >> 1;
                swap(array, mid + fromIndex, l + 1 + fromIndex);
                if (array[l + fromIndex] > array[ir + fromIndex]) {
                    swap(array, l + fromIndex, ir + fromIndex);
                }
                if (array[l + 1 + fromIndex] > array[ir + fromIndex]) {
                    swap(array, l + 1 + fromIndex, ir + fromIndex);
                }
                if (array[l + fromIndex] > array[l + 1 + fromIndex]) {
                    swap(array, l + fromIndex, l + 1 + fromIndex);
                }
                i = l + 1;
                j = ir;
                a = array[l + 1 + fromIndex];
                for (; ; ) {
                    do {
                        i++;
                    } while (array[i + fromIndex] < a);

                    do {
                        j--;
                    } while (array[j + fromIndex] > a);

                    if (j < i) {
                        break;
                    }

                    swap(array, i + fromIndex, j + fromIndex);
                }
                array[l + 1 + fromIndex] = array[j + fromIndex];
                array[j + fromIndex] = a;
                if (j >= k) {
                    ir = j - 1;
                }
                if (j <= k) {
                    l = i;
                }
            }
        }
    }

    /**
     * Returns the k-th sorted element in provided array of {@link Comparable}
     * starting at fromIndex and finishing at toIndex, elements outside this
     * range are ignored.
     * Selecting an element is usually faster than sorting the whole
     * array, and for that reason, when only a few sorted elements are
     * required, this method should be used instead of sort.
     * Because array is passed by reference, after executing this method
     * array is modified so that in k location it contains the k-th sorted
     * elements and on locations array[fromIndex] ... array[k-1 + fromIndex]
     * contains unsorted elements smaller than sorted element
     * array[k + fromIndex],  and on locations
     * array[k+1 + fromIndex] ... array[toIndex-1] contains unsorted
     * elements greater than sorted element array[k + fromIndex].
     *
     * @param k         Position of sorted element to be retrieved.
     * @param array     Array to be used for retrieving k-th sorted element.
     *                  Provided array is passed by reference and modified upon execution of
     *                  this method so that k-th location contains k-th sorted element, and
     *                  array[fromIndex] ... array[k-1 + fromIndex] contains unsorted
     *                  elements smaller than sorted element array[k + fromIndex] and
     *                  array[k+1 + fromIndex] ... array[toIndex-1] contains elements
     *                  greater than sorted element array[k].
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @return The k-th sorted element in provided array.
     * @throws IllegalArgumentException       if k < (toIndex - fromIndex) or
     *                                        fromIndex < toIndex.
     * @throws ArrayIndexOutOfBoundsException if fromIndex or toIndex are
     *                                        outside array boundaries.
     */
    public float select(final int k, final float[] array, final int fromIndex, final int toIndex) {

        if (fromIndex > toIndex) {
            throw new IllegalArgumentException();
        }
        if (fromIndex < 0 || toIndex > array.length) {
            throw new ArrayIndexOutOfBoundsException();
        }

        int i;
        int ir;
        int j;
        int l;
        int mid;
        final var n = toIndex - fromIndex;
        if (k >= n) {
            throw new IllegalArgumentException();
        }

        float a;
        l = 0;
        ir = n - 1;
        for (; ; ) {
            if (ir <= l + 1) {
                if (ir == l + 1 && array[ir + fromIndex] < array[l + fromIndex]) {
                    swap(array, l + fromIndex, ir + fromIndex);
                }
                return array[k + fromIndex];
            } else {
                mid = (l + ir) >> 1;
                swap(array, mid + fromIndex, l + 1 + fromIndex);
                if (array[l + fromIndex] > array[ir + fromIndex]) {
                    swap(array, l + fromIndex, ir + fromIndex);
                }
                if (array[l + 1 + fromIndex] > array[ir + fromIndex]) {
                    swap(array, l + 1 + fromIndex, ir + fromIndex);
                }
                if (array[l + fromIndex] > array[l + 1 + fromIndex]) {
                    swap(array, l + fromIndex, l + 1 + fromIndex);
                }
                i = l + 1;
                j = ir;
                a = array[l + 1 + fromIndex];
                for (; ; ) {
                    do {
                        i++;
                    } while (array[i + fromIndex] < a);

                    do {
                        j--;
                    } while (array[j + fromIndex] > a);

                    if (j < i) {
                        break;
                    }

                    swap(array, i + fromIndex, j + fromIndex);
                }
                array[l + 1 + fromIndex] = array[j + fromIndex];
                array[j + fromIndex] = a;
                if (j >= k) {
                    ir = j - 1;
                }
                if (j <= k) {
                    l = i;
                }
            }
        }
    }

    /**
     * Returns the k-th sorted element in provided array of {@link Comparable}
     * starting at fromIndex and finishing at toIndex, elements outside this
     * range are ignored.
     * Selecting an element is usually faster than sorting the whole
     * array, and for that reason, when only a few sorted elements are
     * required, this method should be used instead of sort.
     * Because array is passed by reference, after executing this method
     * array is modified so that in k location it contains the k-th sorted
     * elements and on locations array[fromIndex] ... array[k-1 + fromIndex]
     * contains unsorted elements smaller than sorted element
     * array[k + fromIndex],  and on locations
     * array[k+1 + fromIndex] ... array[toIndex-1] contains unsorted
     * elements greater than sorted element array[k + fromIndex].
     *
     * @param k         Position of sorted element to be retrieved.
     * @param array     Array to be used for retrieving k-th sorted element.
     *                  Provided array is passed by reference and modified upon execution of
     *                  this method so that k-th location contains k-th sorted element, and
     *                  array[fromIndex] ... array[k-1 + fromIndex] contains unsorted
     *                  elements smaller than sorted element array[k + fromIndex] and
     *                  array[k+1 + fromIndex] ... array[toIndex-1] contains elements
     *                  greater than sorted element array[k].
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @return The k-th sorted element in provided array.
     * @throws IllegalArgumentException       if k < (toIndex - fromIndex) or
     *                                        fromIndex < toIndex.
     * @throws ArrayIndexOutOfBoundsException if fromIndex or toIndex are
     *                                        outside array boundaries.
     */
    public int select(final int k, final int[] array, final int fromIndex, final int toIndex) {

        if (fromIndex > toIndex) {
            throw new IllegalArgumentException();
        }
        if (fromIndex < 0 || toIndex > array.length) {
            throw new ArrayIndexOutOfBoundsException();
        }

        int i;
        int ir;
        int j;
        int l;
        int mid;
        final var n = toIndex - fromIndex;
        if (k >= n) {
            throw new IllegalArgumentException();
        }

        int a;
        l = 0;
        ir = n - 1;
        for (; ; ) {
            if (ir <= l + 1) {
                if (ir == l + 1 && array[ir + fromIndex] < array[l + fromIndex]) {
                    swap(array, l + fromIndex, ir + fromIndex);
                }
                return array[k + fromIndex];
            } else {
                mid = (l + ir) >> 1;
                swap(array, mid + fromIndex, l + 1 + fromIndex);
                if (array[l + fromIndex] > array[ir + fromIndex]) {
                    swap(array, l + fromIndex, ir + fromIndex);
                }
                if (array[l + 1 + fromIndex] > array[ir + fromIndex]) {
                    swap(array, l + 1 + fromIndex, ir + fromIndex);
                }
                if (array[l + fromIndex] > array[l + 1 + fromIndex]) {
                    swap(array, l + fromIndex, l + 1 + fromIndex);
                }
                i = l + 1;
                j = ir;
                a = array[l + 1 + fromIndex];
                for (; ; ) {
                    do {
                        i++;
                    } while (array[i + fromIndex] < a);

                    do {
                        j--;
                    } while (array[j + fromIndex] > a);

                    if (j < i) {
                        break;
                    }

                    swap(array, i + fromIndex, j + fromIndex);
                }
                array[l + 1 + fromIndex] = array[j + fromIndex];
                array[j + fromIndex] = a;
                if (j >= k) {
                    ir = j - 1;
                }
                if (j <= k) {
                    l = i;
                }
            }
        }
    }

    /**
     * Returns the k-th sorted element in provided array of {@link Comparable}
     * starting at fromIndex and finishing at toIndex, elements outside this
     * range are ignored.
     * Selecting an element is usually faster than sorting the whole
     * array, and for that reason, when only a few sorted elements are
     * required, this method should be used instead of sort.
     * Because array is passed by reference, after executing this method
     * array is modified so that in k location it contains the k-th sorted
     * elements and on locations array[fromIndex] ... array[k-1 + fromIndex]
     * contains unsorted elements smaller than sorted element
     * array[k + fromIndex],  and on locations
     * array[k+1 + fromIndex] ... array[toIndex-1] contains unsorted
     * elements greater than sorted element array[k + fromIndex].
     *
     * @param k         Position of sorted element to be retrieved.
     * @param array     Array to be used for retrieving k-th sorted element.
     *                  Provided array is passed by reference and modified upon execution of
     *                  this method so that k-th location contains k-th sorted element, and
     *                  array[fromIndex] ... array[k-1 + fromIndex] contains unsorted
     *                  elements smaller than sorted element array[k + fromIndex] and
     *                  array[k+1 + fromIndex] ... array[toIndex-1] contains elements
     *                  greater than sorted element array[k].
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @return The k-th sorted element in provided array.
     * @throws IllegalArgumentException       if k < (toIndex - fromIndex) or
     *                                        fromIndex < toIndex.
     * @throws ArrayIndexOutOfBoundsException if fromIndex or toIndex are
     *                                        outside array boundaries.
     */
    public long select(final int k, final long[] array, final int fromIndex, final int toIndex) {

        if (fromIndex > toIndex) {
            throw new IllegalArgumentException();
        }
        if (fromIndex < 0 || toIndex > array.length) {
            throw new ArrayIndexOutOfBoundsException();
        }

        int i;
        int ir;
        int j;
        int l;
        int mid;
        final var n = toIndex - fromIndex;
        if (k >= n) {
            throw new IllegalArgumentException();
        }

        long a;
        l = 0;
        ir = n - 1;
        for (; ; ) {
            if (ir <= l + 1) {
                if (ir == l + 1 && array[ir + fromIndex] < array[l + fromIndex]) {
                    swap(array, l + fromIndex, ir + fromIndex);
                }
                return array[k + fromIndex];
            } else {
                mid = (l + ir) >> 1;
                swap(array, mid + fromIndex, l + 1 + fromIndex);
                if (array[l + fromIndex] > array[ir + fromIndex]) {
                    swap(array, l + fromIndex, ir + fromIndex);
                }
                if (array[l + 1 + fromIndex] > array[ir + fromIndex]) {
                    swap(array, l + 1 + fromIndex, ir + fromIndex);
                }
                if (array[l + fromIndex] > array[l + 1 + fromIndex]) {
                    swap(array, l + fromIndex, l + 1 + fromIndex);
                }
                i = l + 1;
                j = ir;
                a = array[l + 1 + fromIndex];
                for (; ; ) {
                    do {
                        i++;
                    } while (array[i + fromIndex] < a);

                    do {
                        j--;
                    } while (array[j + fromIndex] > a);

                    if (j < i) {
                        break;
                    }

                    swap(array, i + fromIndex, j + fromIndex);
                }
                array[l + 1 + fromIndex] = array[j + fromIndex];
                array[j + fromIndex] = a;
                if (j >= k) {
                    ir = j - 1;
                }
                if (j <= k) {
                    l = i;
                }
            }
        }
    }

    /**
     * Computes median of provided array
     * Median is computed by selecting the length / 2 element, hence
     * provided array is modified upon execution of this method containing
     * sorted element at location length / 2, smaller unsorted elements at
     * array[0] ... array[length / 2 - 1], and greater unsorted elements at
     * array[length / 2 + 1] ... array[length - 1].
     *
     * @param array Array to be used for computation of median. This array
     *              is modified after execution of this method.
     * @return Median of provided array.
     */
    public T median(final Comparable[] array) {
        return median(array, 0, array.length);
    }

    /**
     * Computes median of provided array of {@link Comparable}
     * Median is computed by selecting the
     * ((toIndex - fromIndex) + fromIndex) / 2 element, hence
     * provided array is modified upon execution of this method containing
     * sorted element at location ((toIndex - fromIndex) + fromIndex)  / 2,
     * smaller unsorted elements at array[fromIndex] ...
     * array[((toIndex - fromIndex) + fromIndex) / 2 - 1], and greater
     * unsorted elements at
     * array[((toIndex - fromIndex) + fromIndex) / 2 + 1] ...
     * array[toIndex - 1].
     *
     * @param array     Array to be used for computation of median. This array
     *                  is modified after execution of this method.
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @return Median of provided array.
     * @throws IllegalArgumentException       if k < (toIndex - fromIndex) or
     *                                        fromIndex < toIndex.
     * @throws ArrayIndexOutOfBoundsException if fromIndex or toIndex are
     *                                        outside array boundaries.
     */
    @SuppressWarnings("unchecked")
    public T median(final Comparable[] array, final int fromIndex, final int toIndex) {

        return median((T[]) array, fromIndex, toIndex, new ComparatorAndAverager<>() {
            @Override
            public int compare(final T t1, final T t2) {
                final var t1b = (Comparable) t1;
                return t1b.compareTo(t2);
            }

            @Override
            public T average(final T t1, final T t2) {
                if (t1 instanceof ComparableAndAverageable && t2 instanceof ComparableAndAverageable) {
                    return ((ComparableAndAverageable) t1).averageWith(t2);
                }
                if (t1 instanceof Byte b1 && t2 instanceof Byte b2) {
                    return (T) Byte.valueOf((byte) ((b1 + b2) / 2));
                }
                if (t1 instanceof Character c1 && t2 instanceof Character c2) {
                    return (T) Character.valueOf((char) ((c1 + c2) / 2));
                }
                if (t1 instanceof Short c1 && t2 instanceof Short c2) {
                    return (T) Short.valueOf((short) ((c1 + c2) / 2));
                }
                if (t1 instanceof Integer i1 && t2 instanceof Integer i2) {
                    return (T) Integer.valueOf((i1 + i2) / 2);
                }
                if (t1 instanceof Long l1 && t2 instanceof Long l2) {
                    return (T) Long.valueOf((l1 + l2) / 2);
                }
                if (t1 instanceof Float f1 && t2 instanceof Float f2) {
                    return (T) Float.valueOf((f1 + f2) / 2.0f);
                }
                if (t1 instanceof Double d1 && t2 instanceof Double d2) {
                    return (T) Double.valueOf((d1 + d2) / 2.0);
                }

                // for other case, average returns 1st parameter
                return t1;
            }
        });
    }

    /**
     * Computes median of provided array
     * Median is computed by selecting the length / 2 element, hence
     * provided array is modified upon execution of this method containing
     * sorted element at location length / 2, smaller unsorted elements at
     * array[0] ... array[length / 2 - 1], and greater unsorted elements at
     * array[length / 2 + 1] ... array[length - 1].
     *
     * @param array      Array to be used for computation of median. This array
     *                   is modified after execution of this method.
     * @param comparator Determines whether an element is greater or lower
     *                   than another one and also is capable of computing the average
     *                   between two T instances.
     * @return Median of provided array.
     */
    public T median(final T[] array, final ComparatorAndAverager comparator) {
        return median(array, 0, array.length, comparator);
    }

    /**
     * Computes median of provided array
     * Median is computed by selecting the length / 2 element, hence
     * provided array is modified upon execution of this method containing
     * sorted element at location length / 2, smaller unsorted elements at
     * array[0] ... array[length / 2 - 1], and greater unsorted elements at
     * array[length / 2 + 1] ... array[length - 1].
     *
     * @param array Array to be used for computation of median. This array
     *              is modified after execution of this method.
     * @return Median of provided array.
     */
    public double median(final double[] array) {
        return median(array, 0, array.length);
    }

    /**
     * Computes median of provided array.
     * Median is computed by selecting the length / 2 element, hence
     * provided array is modified upon execution of this method containing
     * sorted element at location length / 2, smaller unsorted elements at
     * array[0] ... array[length / 2 - 1], and greater unsorted elements at
     * array[length / 2 + 1] ... array[length - 1].
     *
     * @param array Array to be used for computation of median. This array
     *              is modified after execution of this method.
     * @return Median of provided array.
     */
    public float median(final float[] array) {
        return median(array, 0, array.length);
    }

    /**
     * Computes median of provided array.
     * Median is computed by selecting the length / 2 element, hence
     * provided array is modified upon execution of this method containing
     * sorted element at location length / 2, smaller unsorted elements at
     * array[0] ... array[length / 2 - 1], and greater unsorted elements at
     * array[length / 2 + 1] ... array[length - 1].
     *
     * @param array Array to be used for computation of median. This array
     *              is modified after execution of this method.
     * @return Median of provided array.
     */
    public int median(final int[] array) {
        return median(array, 0, array.length);
    }

    /**
     * Computes median of provided array.
     * Median is computed by selecting the length / 2 element, hence
     * provided array is modified upon execution of this method containing
     * sorted element at location length / 2, smaller unsorted elements at
     * array[0] ... array[length / 2 - 1], and greater unsorted elements at
     * array[length / 2 + 1] ... array[length - 1].
     *
     * @param array Array to be used for computation of median. This array
     *              is modified after execution of this method.
     * @return Median of provided array.
     */
    public long median(final long[] array) {
        return median(array, 0, array.length);
    }


    /**
     * Computes median of provided array.
     * Median is computed by selecting the
     * ((toIndex - fromIndex) + fromIndex) / 2 element, hence
     * provided array is modified upon execution of this method containing
     * sorted element at location ((toIndex - fromIndex) + fromIndex)  / 2,
     * smaller unsorted elements at array[fromIndex] ...
     * array[((toIndex - fromIndex) + fromIndex) / 2 - 1], and greater
     * unsorted elements at
     * array[((toIndex - fromIndex) + fromIndex) / 2 + 1] ...
     * array[toIndex - 1].
     *
     * @param array      Array to be used for computation of median. This array
     *                   is modified after execution of this method.
     * @param fromIndex  Index were sorting starts (inclusive).
     * @param toIndex    Index were sorting stops (exclusive).
     * @param comparator Determines whether an element is greater or lower
     *                   than another one and also is capable of computing the average
     *                   between two T instances.
     * @return Median of provided array.
     * @throws IllegalArgumentException       if fromIndex is greater than toIndex.
     * @throws ArrayIndexOutOfBoundsException if either fromIndex or toIndex are out of bounds.
     */
    public T median(final T[] array, final int fromIndex, final int toIndex,
                    final ComparatorAndAverager comparator) {

        if (fromIndex > toIndex) {
            throw new IllegalArgumentException();
        }
        if (fromIndex < 0 || toIndex > array.length) {
            throw new ArrayIndexOutOfBoundsException();
        }

        final var length = toIndex - fromIndex;
        final var pos1 = length / 2;

        // select pos1 ordered element of v and modifies v so that
        // v(0) ... v(pos1 - 1) < value1 < v(pos1 + 1) ... v(length - 1)
        // where v(0) ... v(pos1 - 1) are unordered elements lower than value1
        // and v(pos1) ... v(length - 1) are unordered elements greater than
        // value1
        final var value1 = select(pos1, array, fromIndex, toIndex, comparator);
        if ((length % 2) == 0) {
            // for even length

            // value2 is the previously ordered element of v, which is the maximum
            // element within v(0) ... v(pos1 - 1)
            var value2 = array[fromIndex];
            for (int i = 1; i < pos1; i++) {
                final var value3 = array[i + fromIndex];
                if (comparator.compare(value3, value2) > 0) {
                    value2 = value3;
                }
            }

            return comparator.average(value1, value2);
        } else {
            // for odd length
            return value1;
        }
    }

    /**
     * Computes median of provided array.
     * Median is computed by selecting the
     * ((toIndex - fromIndex) + fromIndex) / 2 element, hence
     * provided array is modified upon execution of this method containing
     * sorted element at location ((toIndex - fromIndex) + fromIndex)  / 2,
     * smaller unsorted elements at array[fromIndex] ...
     * array[((toIndex - fromIndex) + fromIndex) / 2 - 1], and greater
     * unsorted elements at
     * array[((toIndex - fromIndex) + fromIndex) / 2 + 1] ...
     * array[toIndex - 1].
     *
     * @param array     Array to be used for computation of median. This array
     *                  is modified after execution of this method.
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @return Median of provided array.
     * @throws IllegalArgumentException       if fromIndex is greater than toIndex.
     * @throws ArrayIndexOutOfBoundsException if either fromIndex or toIndex are out of bounds.
     */
    public double median(final double[] array, final int fromIndex, final int toIndex) {

        if (fromIndex > toIndex) {
            throw new IllegalArgumentException();
        }
        if (fromIndex < 0 || toIndex > array.length) {
            throw new ArrayIndexOutOfBoundsException();
        }

        final var length = toIndex - fromIndex;
        final var pos1 = length / 2;

        // select pos1 ordered element of v and modifies v so that
        // v(0) ... v(pos1 - 1) < value1 < v(pos1 + 1) ... v(length - 1)
        // where v(0) ... v(pos1 - 1) are unordered elements lower than value1
        // and v(pos1) ... v(length - 1) are unordered elements greater than
        // value1
        final var value1 = select(pos1, array, fromIndex, toIndex);
        if ((length % 2) == 0) {
            // for even length

            // value2 is the previously ordered element of v, which is the maximum
            // element within v(0) ... v(pos1 - 1)
            var value2 = array[fromIndex];
            for (int i = 1; i < pos1; i++) {
                final var value3 = array[i + fromIndex];
                if (value3 > value2) {
                    value2 = value3;
                }
            }

            return 0.5 * (value1 + value2);
        } else {
            // for odd length
            return value1;
        }
    }

    /**
     * Computes median of provided array.
     * Median is computed by selecting the
     * ((toIndex - fromIndex) + fromIndex) / 2 element, hence
     * provided array is modified upon execution of this method containing
     * sorted element at location ((toIndex - fromIndex) + fromIndex)  / 2,
     * smaller unsorted elements at array[fromIndex] ...
     * array[((toIndex - fromIndex) + fromIndex) / 2 - 1], and greater
     * unsorted elements at
     * array[((toIndex - fromIndex) + fromIndex) / 2 + 1] ...
     * array[toIndex - 1].
     *
     * @param array     Array to be used for computation of median. This array
     *                  is modified after execution of this method.
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @return Median of provided array.
     * @throws IllegalArgumentException       if fromIndex is greater than toIndex.
     * @throws ArrayIndexOutOfBoundsException if either fromIndex or toIndex are out of bounds.
     */
    public float median(final float[] array, final int fromIndex, final int toIndex) {

        if (fromIndex > toIndex) {
            throw new IllegalArgumentException();
        }
        if (fromIndex < 0 || toIndex > array.length) {
            throw new ArrayIndexOutOfBoundsException();
        }

        final var length = toIndex - fromIndex;
        final var pos1 = length / 2;

        // select pos1 ordered element of v and modifies v so that
        // v(0) ... v(pos1 - 1) < value1 < v(pos1 + 1) ... v(length - 1)
        // where v(0) ... v(pos1 - 1) are unordered elements lower than value1
        // and v(pos1) ... v(length - 1) are unordered elements greater than
        // value1
        final var value1 = select(pos1, array, fromIndex, toIndex);
        if ((length % 2) == 0) {
            // for even length

            // value2 is the previously ordered element of v, which is the maximum
            // element within v(0) ... v(pos1 - 1)
            var value2 = array[fromIndex];
            for (int i = 1; i < pos1; i++) {
                final var value3 = array[i + fromIndex];
                if (value3 > value2) {
                    value2 = value3;
                }
            }

            return 0.5f * (value1 + value2);
        } else {
            // for odd length
            return value1;
        }
    }

    /**
     * Computes median of provided array.
     * Median is computed by selecting the
     * ((toIndex - fromIndex) + fromIndex) / 2 element, hence
     * provided array is modified upon execution of this method containing
     * sorted element at location ((toIndex - fromIndex) + fromIndex)  / 2,
     * smaller unsorted elements at array[fromIndex] ...
     * array[((toIndex - fromIndex) + fromIndex) / 2 - 1], and greater
     * unsorted elements at
     * array[((toIndex - fromIndex) + fromIndex) / 2 + 1] ...
     * array[toIndex - 1].
     *
     * @param array     Array to be used for computation of median. This array
     *                  is modified after execution of this method.
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @return Median of provided array.
     * @throws IllegalArgumentException       if fromIndex is greater than toIndex.
     * @throws ArrayIndexOutOfBoundsException if either fromIndex or toIndex are out of bounds.
     */
    public int median(final int[] array, final int fromIndex, final int toIndex) {

        if (fromIndex > toIndex) {
            throw new IllegalArgumentException();
        }
        if (fromIndex < 0 || toIndex > array.length) {
            throw new ArrayIndexOutOfBoundsException();
        }

        final var length = toIndex - fromIndex;
        final var pos1 = length / 2;

        // select pos1 ordered element of v and modifies v so that
        // v(0) ... v(pos1 - 1) < value1 < v(pos1 + 1) ... v(length - 1)
        // where v(0) ... v(pos1 - 1) are unordered elements lower than value1
        // and v(pos1) ... v(length - 1) are unordered elements greater than
        // value1
        final var value1 = select(pos1, array, fromIndex, toIndex);
        if ((length % 2) == 0) {
            // for even length

            // value2 is the previously ordered element of v, which is the maximum
            // element within v(0) ... v(pos1 - 1)
            var value2 = array[fromIndex];
            for (int i = 1; i < pos1; i++) {
                final var value3 = array[i + fromIndex];
                if (value3 > value2) {
                    value2 = value3;
                }
            }

            return (int) (0.5 * ((double) value1 + (double) value2));
        } else {
            // for odd length
            return value1;
        }
    }

    /**
     * Computes median of provided array.
     * Median is computed by selecting the
     * ((toIndex - fromIndex) + fromIndex) / 2 element, hence
     * provided array is modified upon execution of this method containing
     * sorted element at location ((toIndex - fromIndex) + fromIndex)  / 2,
     * smaller unsorted elements at array[fromIndex] ...
     * array[((toIndex - fromIndex) + fromIndex) / 2 - 1], and greater
     * unsorted elements at
     * array[((toIndex - fromIndex) + fromIndex) / 2 + 1] ...
     * array[toIndex - 1].
     *
     * @param array     Array to be used for computation of median. This array
     *                  is modified after execution of this method.
     * @param fromIndex Index were sorting starts (inclusive).
     * @param toIndex   Index were sorting stops (exclusive).
     * @return Median of provided array.
     * @throws IllegalArgumentException       if fromIndex is greater than toIndex.
     * @throws ArrayIndexOutOfBoundsException if either fromIndex or toIndex are out of bounds.
     */
    public long median(final long[] array, final int fromIndex, final int toIndex) {

        if (fromIndex > toIndex) {
            throw new IllegalArgumentException();
        }
        if (fromIndex < 0 || toIndex > array.length) {
            throw new ArrayIndexOutOfBoundsException();
        }

        final int length = toIndex - fromIndex;
        final var pos1 = length / 2;

        // select pos1 ordered element of v and modifies v so that
        // v(0) ... v(pos1 - 1) < value1 < v(pos1 + 1) ... v(length - 1)
        // where v(0) ... v(pos1 - 1) are unordered elements lower than value1
        // and v(pos1) ... v(length - 1) are unordered elements greater than
        // value1
        final var value1 = select(pos1, array, fromIndex, toIndex);
        if ((length % 2) == 0) {
            // for even length

            // value2 is the previously ordered element of v, which is the maximum
            // element within v(0) ... v(pos1 - 1)
            var value2 = array[fromIndex];
            for (int i = 1; i < pos1; i++) {
                final var value3 = array[i + fromIndex];
                if (value3 > value2) {
                    value2 = value3;
                }
            }

            return (long) (0.5 * ((double) value1 + (double) value2));
        } else {
            // for odd length
            return value1;
        }
    }

    /**
     * Returns sorting method of an implementation of this class.
     *
     * @return Sorting method.
     */
    public abstract SortingMethod getMethod();

    /**
     * Creates a Sorter instance using DEFAULT_SORTING_METHOD.
     *
     * @return A sorter instance.
     */
    public static  Sorter create() {
        return create(DEFAULT_SORTING_METHOD);
    }

    /**
     * Creates a Sorter instance using provided sorting method.
     *
     * @param method Method to be used for sorting.
     * @return A sorter instance.
     */
    public static  Sorter create(final SortingMethod method) {
        return switch (method) {
            case STRAIGHT_INSERTION_SORTING_METHOD -> new StraightInsertionSorter<>();
            case SHELL_SORTING_METHOD -> new ShellSorter<>();
            case HEAPSORT_SORTING_METHOD -> new HeapsortSorter<>();
            case QUICKSORT_SORTING_METHOD -> new QuicksortSorter<>();
            default -> new SystemSorter<>();
        };
    }

    /**
     * Returns a new array containing original indices ordered from 0
     * to length-1.
     *
     * @param length length of returned array.
     * @return Array with indices in natural order.
     */
    protected int[] getInitialIndicesVector(final int length) {
        final var out = new int[length];

        for (int i = 0; i < length; i++) {
            out[i] = i;
        }

        return out;
    }

    /**
     * Swaps values in array at locations posA and posB.
     *
     * @param arr  array where values are swapped.
     * @param posA Location to be swapped.
     * @param posB Location to be swapped.
     */
    protected void swap(final T[] arr, final int posA, final int posB) {
        final var value = arr[posA];
        arr[posA] = arr[posB];
        arr[posB] = value;
    }

    /**
     * Swaps values in array at locations posA and posB.
     *
     * @param arr  array where values are swapped.
     * @param posA Location to be swapped.
     * @param posB Location to be swapped.
     */
    protected void swap(final double[] arr, final int posA, final int posB) {
        final var value = arr[posA];
        arr[posA] = arr[posB];
        arr[posB] = value;
    }

    /**
     * Swaps values in array at locations posA and posB.
     *
     * @param arr  array where values are swapped.
     * @param posA Location to be swapped.
     * @param posB Location to be swapped.
     */
    protected void swap(final float[] arr, final int posA, final int posB) {
        final var value = arr[posA];
        arr[posA] = arr[posB];
        arr[posB] = value;
    }

    /**
     * Swaps values in array at locations posA and posB.
     *
     * @param arr  array where values are swapped.
     * @param posA Location to be swapped.
     * @param posB Location to be swapped.
     */
    protected void swap(final int[] arr, final int posA, final int posB) {
        final var value = arr[posA];
        arr[posA] = arr[posB];
        arr[posB] = value;
    }

    /**
     * Swaps values in array at locations posA and posB.
     *
     * @param arr  array where values are swapped.
     * @param posA Location to be swapped.
     * @param posB Location to be swapped.
     */
    protected void swap(final long[] arr, final int posA, final int posB) {
        final var value = arr[posA];
        arr[posA] = arr[posB];
        arr[posB] = value;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy