
import java.util.Arrays;

public class Tugas2MergeSort {
    /** Membuat Function Sort 
     *  Berfungsi untuk membagi array menjadi 2 bagian secara rekursif,
     *  Kemudian mengurutkannya secara Descending dari Terbesar
     *  Sampai Terkecil
     **/
    
    void sort(int arr[], int l, int r){
        
        System.out.println();
        System.out.println("Memanggil Function Sort(arr, " + l + ", " + r + ") : bagian saat ini " + Arrays.toString(Arrays.copyOfRange(arr, l, r + 1)));
        
        if(l < r)
        {
            int m = (l + r) / 2;
            System.out.println("  Membagi Menjadi 2 Bagian : [" + l + ".." + m + "] dan [" + (m+1) + ".." + r + "]"); 
            
            /** Urutkan Bagian Kiri **/
            sort(arr, l, m);
            
            /** Urutkan Bagian Kanan **/
            sort(arr, m + 1, r);
            
            /** Gabungkan 2 Bagian yang sudah terurut secara Descending **/
            merge(arr, l, m, r);
            
        } else {
            /** Tidak perlu diurutkan, karena hanya ada 1 elemen saja **/
            System.out.println("  Bagian["+l+".."+ r +"] hanya satu elemen, tidak perlu diurutkan");
        }
        
    }
    
    
    
    /** Membuat Function Merge 
     * Untuk menggabungkan 2 bagian array yang sudah terurut 
     */
    
    void merge(int[] arr, int l, int m, int r) 
    {
        int n1 = m - l + 1;
        int n2 = r - m;
        
        int L[] = new int[n1];
        int R[] = new int[n2];
        
        /** Menyalin elemen ke array sementara L & R **/
        for(int i = 0; i < n1; i++)
        {
            L[i] = arr[l + i];
        }
        
        for(int j = 0; j < n2; j++){
            R[j] = arr[m + 1 + j];
        }
        
        System.out.println();
        System.out.println("Memulai proses Merge untuk indeks ["+l+".."+m+"] dan ["+(m+1)+".."+r+"]");
        System.out.println("  Bagian Kiri (L) : " + Arrays.toString(L));
        System.out.println("  Bagian Kanan (R) : " + Arrays.toString(R));
        
        int i = 0, j = 0;
        int k = l;
        
        /** Membandingkan elemen dari L dan R lalu dimasukan kedalam array utama arr **/
        while (i < n1 && j < n2)
        {
            System.out.println("  Bandingkan L["+i+"] = " + L[i] + " dengan R["+j+"] = " + R[j]);
            
            if(L[i] >= R[j])
            {
                arr[k] = L[i];
                System.out.println("    Ambil L["+i+"] = " + L[i] + " ke arr["+k+"]");
                i++;
            } else {
                arr[k] = R[j];
                System.out.println("    Ambil R["+j+"] = " + R[j] + " ke arr["+k+"]");
                j++;
            }
            k++;
            System.out.println("      Hasil sementara : " + Arrays.toString(arr));
        }
        
        /** Jika masih ada sisa elemen di L, masukkan ke array utama arr. **/
        while(i < n1)
        {
            arr[k] = L[i];
            i++;
            k++;
        }
        /** Jika masih ada sisa elemen di R, masukkan ke array utama arr. **/
        while(j < n2)
        {
            arr[k] = R[j];
            j++;
            k++;
        }
        
        System.out.println("  Selesai merge untuk indeks ["+l+".."+r+"]: " + Arrays.toString(Arrays.copyOfRange(arr, l, r + 1)));
        
    }
    
    
    /** Membuat Function untuk mencetak isi array, selain menggunakan Library Arrays.**/
    static void printArray(int arr[])
    {
        int n = arr.length;
        for(int i = 0; i < n; i++)
        {
            System.out.print(arr[i] + " ");
        }
        System.out.println();
    }
    
    
    /** Membuat Function Main **/
    public static void main(String args[]) {
        /** Membuat Variabel yang akan diurutkan menggunakan merge sort **/
        int a1 = 25;
        int a2 = 7;
        int a3 = 18;
        int a4 = 42;
        int a5 = 13;
        int a6 = 9;
        int a7 = 31;
        int a8 = 2;
        
        int arr[] = {a1, a2, a3, a4, a5, a6, a7, a8};
        
        /** menampilkan array awal **/
        System.out.println("Array awal : ");
        printArray(arr);
        
        /** Process Merge Sort **/
        Tugas2MergeSort ob = new Tugas2MergeSort();
        ob.sort(arr, 0, arr.length - 1);
        
        System.out.println();
        System.out.println("Hasil array menggunakan Merge Sort secara Descending : ");
        printArray(arr);
        
        /** Analisa Algoritma Merge Sort **/
        String hasilAnalisa = """
                              ==================================================
                              KESIMPULAN ALGORITMA MERGE SORT
                              ==================================================
                              
                              Merge Sort adalah algoritma pengurutan dengan konsep
                              'divide dan conquer', dimana : 
                              - Membagi array menjadi dua bagian secara rekursif (divide)
                              - Mengurutkan kembali tiap bagian yang terpisah
                              - Menggabungkan kembali 2 bagian secara terurut.
                              
                              Kelebihan :
                              - Stabil dan efisien untuk data besar
                              - Waktu eksekusi konsisten di semua kasus
                              
                              Kekurangan : 
                              - Membutuhkan memori tambahan (untuk array sementara)
                              - Kurang efisien untuk data kecil
                              
                              Dari Source Code ini, Contoh Hasilnya : 
                              Array Awal : [25, 7, 18, 42, 13, 9, 31, 2]
                              Hasil Akhirnya : [42, 31, 25, 18, 13, 9, 7, 2]
                              """;
        
        System.out.println(hasilAnalisa);

    }
    
}
