package task6;

import org.jetbrains.annotations.NotNull;
import util.Util;

public class SorterAI {
    public void sort(int[] array) {
        if (array == null || array.length <= 1) return;
        introsort(array, 0, array.length - 1, 2 * (int) Math.floor(Math.log(array.length) / Math.log(2)));
    }

    private void introsort(int[] array, int low, int high, int depthLimit) {
        if (high - low < 16) {
            insertionSort(array, low, high);
            return;
        }
        if (depthLimit == 0) {
            heapsort(array, low, high);
            return;
        }
        int pivot = partition(array, low, high);
        introsort(array, low, pivot - 1, depthLimit - 1);
        introsort(array, pivot + 1, high, depthLimit - 1);
    }

    private int partition(int @NotNull [] array, int low, int high) {
        int mid = low + (high - low) / 2;
        if (array[mid] < array[low]) swap(array, low, mid);
        if (array[high] < array[low]) swap(array, low, high);
        if (array[mid] < array[high]) swap(array, mid, high);
        int pivot = array[high];

        int i = low - 1;
        for (int j = low; j < high; j++) {
            if (array[j] <= pivot) {
                swap(array, ++i, j);
            }
        }
        swap(array, i + 1, high);
        return i + 1;
    }

    private void heapsort(int[] array, int low, int high) {
        int n = high - low + 1;
        for (int i = n / 2 - 1; i >= 0; i--)
            heapify(array, low, n, i);
        for (int i = n - 1; i > 0; i--) {
            swap(array, low, low + i);
            heapify(array, low, i, 0);
        }
    }

    private void heapify(int[] array, int offset, int n, int i) {
        int largest = i;
        int left = 2 * i + 1;
        int right = 2 * i + 2;
        if (left < n && array[offset + left] > array[offset + largest]) largest = left;
        if (right < n && array[offset + right] > array[offset + largest]) largest = right;
        if (largest != i) {
            swap(array, offset + i, offset + largest);
            heapify(array, offset, n, largest);
        }
    }

    private void insertionSort(int[] array, int low, int high) {
        for (int i = low + 1; i <= high; i++) {
            int key = array[i];
            int j = i - 1;
            while (j >= low && array[j] > key) {
                array[j + 1] = array[j];
                j--;
            }
            array[j + 1] = key;
        }
    }

    private void swap(int @NotNull [] array, int i, int j) {
        int temp = array[i];
        array[i] = array[j];
        array[j] = temp;
    }

    public static final void main(String[] args) {
        int[] array = new int[1000000000];
        Util util = new Util();
        util.fillArrayRandom(array, 100000000);
        SorterAI mySorter = new SorterAI();
        mySorter.sort(array);
    }
}
