package com.hithomelabs.princeton1.module5;

public class Shell<E> extends AbstractCustomSorts<E> {

    // * * sample metadata class to compare no. of sorts and compares, shell sort vs insertion sort
    private class MetaData{

        int compares;
        int swaps;
    }

/*
 * * 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) {
        MetaData metaData = new MetaData();
        for(int i=arr.length/10; i>=0; i--){
            hsort(arr, 3*i+1, metaData);
        }
        System.out.println("Array sorted (shell sort) with " + metaData.compares + " compares and " + metaData.swaps + " swaps");
    }

    private void hsort(E[] arr, int h, MetaData metadata) {
        for(int i=0; i<arr.length; i=i+h){
            int k = i;
            for(int j=i-h; j>=0; j=j-h){
                metadata.compares++;
                if(less((Comparable<E>) arr[k], arr[j])){
                    exch(arr, k, j);
                    k = j;
                    metadata.swaps++;
                }
                else{
                    break;
                }

            }
        }
    }
    /*
     ! 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){
        MetaData metaData = new MetaData();
        int h = 1;
        hsort(arr, h, metaData);
        System.out.println("Array sorted (insertion sort) with " + metaData.compares + " compares and " + metaData.swaps + " swaps");
    }

}
