See the [next iteration](https://codereview.stackexchange.com/questions/97008/bottom-up-mergesort-in-java-follow-up).

I have this iterative mergesort (a.k.a. ___bottom-up mergesort___).

__BottomUpMergesort.java__:

    package net.coderodde.util.sorting;

    import java.util.Arrays;
    import java.util.Comparator;

    /**
     * This class provides static methods for sorting object arrays using 
     * bottom-up merge sort. The algorithm used is a bottom-up merge sort.
     * 
     * @author Rodion "rodde" Efremov
     * @version 1.6
     */
    public class BottomUpMergesort {

        /**
         * Specifies the maximum length of a chunk which is sorted using insertion
         * sort.
         */
        private static final int INSERTIONSORT_THRESHOLD = 13;

        /**
         * Sorts the range {@code array[fromIndex], array[fromIndex + 1], ...,
         * array[toIndex - 2], array[toIndex - 1]}.
         * 
         * @param <T>       the actual array component type.
         * @param array     the array holding the target range.
         * @param fromIndex the starting (inclusive) index of the range to sort.
         * @param toIndex   the ending (exclusive) index of the range to sort. 
         * @param cmp       the object comparator.
         */
        public static <T> void sort(T[] array,
                                    int fromIndex,
                                    int toIndex,
                                    Comparator<? super T> cmp) {
            if (toIndex - fromIndex < 2) {
                // Trivially sorted or indices ass-basckwards.
                return;
            }

            // Create the auxiliary buffer.
            int rangeLength = toIndex - fromIndex;
            T[] buffer = Arrays.copyOfRange(array, fromIndex, toIndex);

            // Find out how many merge passes we need to do over the input range.
            int runs = rangeLength / INSERTIONSORT_THRESHOLD +
                      (rangeLength % INSERTIONSORT_THRESHOLD != 0 ? 1 : 0);
            int mergePasses = getMergePassAmount(runs);

            // Set up the state.
            T[] source;
            T[] target;
            int sourceOffset;
            int targetOffset;

            if (mergePasses % 2 == 0) {
                // If here, we will do an even amount of merge passes.
                source = array;
                target = buffer;
                sourceOffset = fromIndex;
                targetOffset = 0;
            } else {
                // If here, we will do an odd amount of merge passes.
                source = buffer;
                target = array;
                sourceOffset = 0;
                targetOffset = fromIndex;
            }

            // Create the initial runs.
            for (int i = 0; i < runs - 1; ++i) {
                int tmpIndex = sourceOffset + i * INSERTIONSORT_THRESHOLD;
                insertionSort(source, 
                              tmpIndex, 
                              tmpIndex + INSERTIONSORT_THRESHOLD,
                              cmp);
            }

            // Do not forget the last (the righmost) run. Note, that the length of
            // the last run may vary between 1 and INSERTIONS_SORT_THRESHOLD, 
            // inclusively.
            int lastRunStartIndex = sourceOffset + (runs - 1) * 
                                    INSERTIONSORT_THRESHOLD;
            insertionSort(source, 
                          lastRunStartIndex,
                          Math.min(lastRunStartIndex + INSERTIONSORT_THRESHOLD, 
                                   sourceOffset + rangeLength),
                          cmp);

            // Initial runs are ready to be merged. 'runWidth <<= 1' multiplies
            // 'runWidth' by 2.
            for (int runWidth = INSERTIONSORT_THRESHOLD; 
                     runWidth < rangeLength;
                     runWidth <<= 1) {

                int runIndex = 0;

                for (; runIndex < runs - 1; runIndex += 2) {
                    // Set up the indices.
                    int leftIndex = sourceOffset + runIndex * runWidth;
                    int leftBound = leftIndex + runWidth;
                    int rightBound = Math.min(leftBound + runWidth, 
                                              rangeLength + sourceOffset);
                    int targetIndex = targetOffset + runIndex * runWidth;

                    // Perform the actual merging.
                    merge(source,
                          target,
                          leftIndex,
                          leftBound,
                          rightBound,
                          targetIndex,
                          cmp);
                }

                if (runIndex < runs) {
                    // 'runIndex' is the index of the "orphan" run at the end of the
                    // range being sorted. Since it may appear in the opposite 
                    // array as two non-merged runs, we have to simply copy this 
                    // orphan run to the opposite array.
                    System.arraycopy(source,
                                     sourceOffset + runIndex * runWidth,
                                     target,
                                     targetOffset + runIndex * runWidth,
                                     rangeLength - runIndex * runWidth);
                }

                runs = (runs >>> 1) + (runs % 2 == 0 ? 0 : 1);
                // Change the roles of the arrays.
                T[] tmparr = source;
                source = target;
                target = tmparr;
                int tmp = sourceOffset;
                sourceOffset = targetOffset;
                targetOffset = tmp;
            }
        }

        /**
         * Sorts the entire array.
         * 
         * @param <T>   the array component type.
         * @param array the array to sort.
         * @param cmp   the comparator.
         */
        public static <T> void sort(T[] array, Comparator<? super T> cmp) {
            sort(array, 0, array.length, cmp);
        }

        /**
         * Sorts the range {@code array[fromIndex,], array[fromIndex + 1], ...,
         * array[toIndex - 2], array[toIndex - 1]} using insertion sort. This 
         * implementation is <b>stable</b>.
         * 
         * @param <T>       the array component type.
         * @param array     the array holding the requested range.
         * @param fromIndex the starting (inclusive) index.
         * @param toIndex   the ending (exclusive) index.
         * @param cmp       the array component comparator.
         */
        public static <T> void insertionSort(T[] array,
                                             int fromIndex,
                                             int toIndex,
                                             Comparator<? super T> cmp) {
            for (int i = fromIndex + 1; i < toIndex; ++i) {
                T element = array[i];
                int j = i;

                for (; j > fromIndex && cmp.compare(array[j - 1], element) > 0; --j) {
                    array[j] = array[j - 1];
                }

                array[j] = element;
            }
        }

        /**
         * Returns the amount of merge passes needed to sort a range containing 
         * {@code runs} runs. (A run is any contiguous, strictly descending or
         * ascending subsequence.
         * 
         * @param  runs the amount of runs in the target range.
         * @return the amount of needed merge passes.
         */
        private static int getMergePassAmount(int runs) {
            return 32 - Integer.numberOfLeadingZeros(runs - 1);
        }

        /**
         * Merges the sorted ranges {@code source[leftIndex, leftBound)} and
         * {@code source[rightIndex, rightBound)} putting the result to 
         * {@code target} starting from component with index {@code targetIndex}.
         * 
         * @param <T>         the array component type.
         * @param source      the source array.
         * @param target      the target array.
         * @param leftIndex   the (inclusive) starting index of the left run.
         * @param leftBound   the (exclusive) ending index of the left run.
         * @param rightIndex  the (inclusive) starting index of the right run.
         * @param rightBound  the (exclusive) ending index of the right run.
         * @param targetIndex the starting index of the result run in the target
         *                     array.
         * @param cmp         the element comparator.
         */
        private static <T> void merge(T[] source,
                                      T[] target,
                                      int leftIndex,
                                      int leftBound,
                                      int rightBound,
                                      int targetIndex,
                                      Comparator<? super T> cmp) {
            int rightIndex = leftBound;

            while (leftIndex < leftBound && rightIndex < rightBound) {
                target[targetIndex++] = 
                        cmp.compare(source[rightIndex], source[leftIndex]) < 0 ?
                        source[rightIndex++] :
                        source[leftIndex++];
            }

            System.arraycopy(source, 
                             leftIndex, 
                             target, 
                             targetIndex, 
                             leftBound - leftIndex);

            System.arraycopy(source, 
                             rightIndex, 
                             target, 
                             targetIndex, 
                             rightBound - rightIndex);
        }
    }

__Utils.java__:

    package net.coderodde.util.sorting;

    import java.util.Comparator;
    import java.util.Random;

    /**
     * This class contains some static utility methods for working with arrays.
     * 
     * @author Rodion "rodde" Efremov
     * @version 1.6
     */
    public class Utils {


        /**
         * Tests whether the range {@code array[fromIndex], array[fromIndex + 1],
         * ..., array[toIndex - 2], array[toIndex - 1]} is sorted into ascending 
         * order as specified by {@code cmp}.
         * 
         * @param <T>       the array component type.
         * @param array     the array holding the target range.
         * @param fromIndex the starting (inclusive) index.
         * @param toIndex   the ending (exclusive) index.
         * @param cmp       the element comparator.
         * @return          {@code true} only if the requested range is sorted.
         */
        public static <T> boolean isSorted(T[] array,
                                           int fromIndex,
                                           int toIndex,
                                           Comparator<? super T> cmp) {
            for (int i = fromIndex; i < toIndex - 1; ++i) {
                if (cmp.compare(array[i], array[i + 1]) > 0) {
                    return false;
                }
            }

            return true;
        }

        /**
         * Tests whether the array {@code array} is sorted into ascending order as
         * specified by {@code cmp}.
         * 
         * @param <T>   the array component type.
         * @param array the array holding the target range.
         * @param cmp   the element comparator.
         * @return      {@code true} only if the entire array is sorted.
         */
        public static <T> boolean isSorted(T[] array, Comparator<? super T> cmp) {
            return isSorted(array, 0, array.length, cmp);
        }

        /**
         * Returns {@code true} if the two input arrays are of the same length, and
         * both have identical array components.
         * 
         * @param <T>  the array component type.
         * @param arr1 the first array.
         * @param arr2 the second array.
         * @return {@code true} if the two arrays have identical contents.
         */
        public static <T> boolean arraysIdentical(T[] arr1, T[] arr2) {
            if (arr1.length != arr2.length) {
                return false;
            }

            for (int i = 0; i < arr1.length; ++i) {
                if (arr1[i] != arr2[i]) {
                    return false;
                }
            }

            return true;
        }

        /**
         * This method creates a random array of integers.
         * 
         * @param size the length of the result array.
         * @param random the instance of {@link java.util.Random}.
         * @return a random array.
         */
        public static Integer[] createRandomIntegerArray(int size, Random random) {
            Integer[] ret = new Integer[size];

            for (int i = 0; i < size; ++i) {
                ret[i] = random.nextInt();
            }

            return ret;
        }
    }

__Demo.java__:

    import java.util.Arrays;
    import java.util.Random;
    import static net.coderodde.util.sorting.Utils.isSorted;
    import static net.coderodde.util.sorting.Utils.arraysIdentical;
    import static net.coderodde.util.sorting.Utils.createRandomIntegerArray;
    import net.coderodde.util.sorting.BottomUpMergesort;

    public class Demo {

        private static final int SIZE = 2000000;

        public static void main(String[] args) {
            long seed = System.currentTimeMillis();
            Random random = new Random(seed);
            Integer[] array1 = createRandomIntegerArray(SIZE, random);
            Integer[] array2 = array1.clone();
            System.out.println("Seed: " + seed);

            //// java.util.Arrays.sort
            long ta = System.currentTimeMillis();
            Arrays.sort(array1, Integer::compare);
            long tb = System.currentTimeMillis();

            System.out.println(
                    "java.util.Arrays.sort() in " + (tb - ta) + " ms. Sorted: " +
                    isSorted(array1, 2, 9, Integer::compare));

            //// net.coderodde.util.sorting.BottomUpMergesort.sort
            ta = System.currentTimeMillis();
            BottomUpMergesort.sort(array2, Integer::compare);
            tb = System.currentTimeMillis();

            System.out.println(
                    "net.coderodde.util.sorting.BottomUpMergesort.sort() " +
                    (tb - ta) + " ms. Sorted: " + 
                    isSorted(array2, 2, 9, Integer::compare));

            System.out.println(
                    "Arrays identical: " + arraysIdentical(array1, array2));
        }
    }

And the entire story is here:

    git clone [email protected]:coderodde/BottomUpMergesort.git && cd BottomUpMergesort && mvn test && mvn exec:java

So, what do you think?