/**
 * Kalkulator latency vs throughput atas model tetap: hukum Little
 * (L = lambda x W), persentil dari sampel latensi beku, dan trade-off
 * batching dengan biaya tetap. Seluruh angka adalah hitungan murni.
 */
public class HukumLittle {

    public static void main(String[] args) {
        // ---- 1. Hukum Little: L (in-flight) = lambda x W ----
        System.out.println("== 1. Hukum Little: L (in-flight) = lambda x W ==");
        System.out.printf("%6s %8s %16s%n", "L", "W (ms)", "lambda (req/s)");
        for (int[] baris : new int[][] {{8, 50}, {8, 200}, {16, 50}, {16, 200}}) {
            double lambda = 1000.0 * baris[0] / baris[1];
            System.out.printf("%6d %8d %16.0f%n", baris[0], baris[1], lambda);
        }
        double butuh = 100.0 * 100 / 1000; // lambda 100 req/s, W 100 ms
        System.out.println("kebutuhan terbalik: target 100 req/s pada W = 100 ms butuh L = "
            + (int) Math.ceil(butuh));
        System.out.println();

        // ---- 2. Persentil dari 20 sampel latensi tetap ----
        int[] latensi = {12, 12, 13, 13, 13, 14, 14, 14, 15, 15,
                         16, 16, 17, 18, 19, 21, 24, 30, 48, 96};
        int total = 0;
        for (int x : latensi) {
            total += x;
        }
        System.out.println("== 2. Distribusi latensi: 20 sampel tetap (ms) ==");
        System.out.printf("mean   = %.1f%n", (double) total / latensi.length);
        System.out.println("p50    = " + persentil(latensi, 50));
        System.out.println("p90    = " + persentil(latensi, 90));
        System.out.println("p99    = " + persentil(latensi, 99)
            + "   (pada 20 sampel, p99 = nilai maksimum)");
        int duaTermahal = latensi[latensi.length - 1] + latensi[latensi.length - 2];
        System.out.printf("10%% sampel termahal menyumbang %.1f%% total waktu tunggu%n",
            100.0 * duaTermahal / total);
        System.out.println();

        // ---- 3. Trade-off batching: biaya tetap 2 ms + 1 ms per item ----
        System.out.println("== 3. Batching: biaya tetap 2 ms + 1 ms per item ==");
        System.out.printf("%8s %12s %9s%n", "ukuran", "item/s", "ms");
        for (int batch : new int[] {1, 10, 25, 50}) {
            double waktuMs = 2.0 + batch;
            double throughput = 1000.0 * batch / waktuMs;
            System.out.printf("%8d %12.0f %9.0f%n", batch, throughput, waktuMs);
        }
        System.out.println("batch 10 menangkap " + Math.round(100.0 * 10 / 12)
            + "% dari throughput maksimal teoretis (1000 item/s)");
        System.out.println();
        System.out.println("Kesimpulan: batching menaikkan throughput dan latensi");
        System.out.println("bersamaan; SLA yang kita pegang menentukan pilihan.");
    }

    // Persentil nearest-rank: nilai pada posisi ceil(p/100 x n) setelah terurut
    static int persentil(int[] data, double p) {
        int posisi = (int) Math.ceil(p / 100.0 * data.length);
        return data[posisi - 1];
    }
}
