package com.hithomelabs.princeton1.module5;

import java.util.ArrayList;
import java.util.Comparator;

public class Shell<E> implements MeasurableSort<E> {

/*
 * * We will be performing h sort
 * * Suppose or function to determine subsequent values of h is:
 * * h(i+1) = 3*h(i) + 1; h = 1, 4, 13, 40, 121, ....
 * * h will never be perfectly divisible by 3; further dividing h[i]/3 will give h[i-1]
 * * We want to h-sort using larges value of h smaller than N, followed by smaller values of h
 * * If N = 100, we h-sort by 40, meaning every 40th element will be in order
 * * Then we h-sort by 13, meaning every 40th and every 13th element will be in order and every 40th element will be in order
 * * Finally, when h will come out as 1, it will be same as insertion sort, however by that time the array will almost be sorted
 * * Insertion sort for an almost sorted array is linear time.
 * * As a case study we implement both insertion sort and selection sort for an array of 1000 nos.
 * * And check number of compares and no. of exchanges in each case
 */
    @Override
    public void sort(E[] arr) {
        coreSortLogic(arr,null,null);
    }

    private void coreSortLogic(E[] arr, Comparator<E> cmp, SortingMetaData metaData){
        int N = arr.length;
        int h = 1;
        // * * Calculates the largest value of h greater than n
        while (3 * h + 1 < N) {
            h = 3 * h + 1;
        }
        while (h >= 1) {
            h = hsort(arr, h, cmp, metaData);
            h = h / 3;
        }
    }

    private int hsort(E[] arr, int h, Comparator<E> cmp, SortingMetaData metaData) {
        int N  = arr.length;
        for(int i = h; i < N; i++){
            int j = i;
            while(j >= h && less(arr[j], arr[j-h], cmp, metaData)){
                exch(arr, j, j-h, metaData);
                j = j - h;
            }
        }
        return h;
    }
    /*
     * Sample implementation of insertion sort as h-sort of h = 1
     * Will just be comparing the number of saps taken across both implementations
    */
    public void insertionSort(E[] arr){
        int h = 1;
        h = hsort(arr, h, null, null);
    }

    @Override
    public void sort(E[] arr, Comparator<E> cmp, SortingMetaData metaData) {
        if (metaData != null)
            metaData.startTime();
        coreSortLogic(arr, cmp, metaData);
        if (metaData != null)
            metaData.endTime();
    }

    @Override
    public void sort(E[] arr, Comparator<E> cmp) {
        coreSortLogic(arr, cmp, null);
    }
}
